diff --git a/.eslintrc.js b/.eslintrc.js index 2edd9f5d433f22..6b3378d44cb81b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -219,6 +219,8 @@ module.exports = { // for absolute imports until fixed in // https://github.com/elastic/kibana/issues/36096 '!src/core/server/*.test.mocks{,.ts}', + + 'target/types/**', ], allowSameFolder: true, errorMessage: @@ -491,7 +493,6 @@ module.exports = { '.eslintrc.js', '**/webpackShims/**/*.js', 'packages/kbn-plugin-generator/**/*.js', - 'packages/kbn-plugin-helpers/**/*.js', 'packages/kbn-eslint-import-resolver-kibana/**/*.js', 'packages/kbn-eslint-plugin-eslint/**/*', 'x-pack/gulpfile.js', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 19116977756f48..85f490bc366b9e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -127,9 +127,14 @@ /src/legacy/server/sass/ @elastic/kibana-operations /src/legacy/server/utils/ @elastic/kibana-operations /src/legacy/server/warnings/ @elastic/kibana-operations +/.ci/es-snapshots/ @elastic/kibana-operations +/vars/ @elastic/kibana-operations # Quality Assurance /src/dev/code_coverage @elastic/kibana-qa +/test/functional/services/common @elastic/kibana-qa +/test/functional/services/lib @elastic/kibana-qa +/test/functional/services/remote @elastic/kibana-qa # Platform /src/core/ @elastic/kibana-platform diff --git a/Jenkinsfile b/Jenkinsfile index 958ced8c6195a6..f435b18c6d824e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,7 @@ library 'kibana-pipeline-library' kibanaLibrary.load() -kibanaPipeline(timeoutMinutes: 135, checkPrChanges: true) { +kibanaPipeline(timeoutMinutes: 155, checkPrChanges: true) { ciStats.trackBuild { githubPr.withDefaultPrComments { catchError { @@ -53,7 +53,9 @@ kibanaPipeline(timeoutMinutes: 135, checkPrChanges: true) { } } - retryable.printFlakyFailures() - kibanaPipeline.sendMail() + if (params.NOTIFY_ON_FAILURE) { + slackNotifications.onFailure() + kibanaPipeline.sendMail() + } } } diff --git a/docs/development/core/public/kibana-plugin-core-public.coresetup.doclinks.md b/docs/development/core/public/kibana-plugin-core-public.coresetup.doclinks.md new file mode 100644 index 00000000000000..b239319c427fe4 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.coresetup.doclinks.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [CoreSetup](./kibana-plugin-core-public.coresetup.md) > [docLinks](./kibana-plugin-core-public.coresetup.doclinks.md) + +## CoreSetup.docLinks property + +[DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) + +Signature: + +```typescript +docLinks: DocLinksSetup; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.coresetup.md b/docs/development/core/public/kibana-plugin-core-public.coresetup.md index 870fa33dce9000..4f981b5a40139f 100644 --- a/docs/development/core/public/kibana-plugin-core-public.coresetup.md +++ b/docs/development/core/public/kibana-plugin-core-public.coresetup.md @@ -18,6 +18,7 @@ export interface CoreSetupApplicationSetup | [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) | | [context](./kibana-plugin-core-public.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-core-public.contextsetup.md) | +| [docLinks](./kibana-plugin-core-public.coresetup.doclinks.md) | DocLinksSetup | [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) | | [fatalErrors](./kibana-plugin-core-public.coresetup.fatalerrors.md) | FatalErrorsSetup | [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) | | [getStartServices](./kibana-plugin-core-public.coresetup.getstartservices.md) | StartServicesAccessor<TPluginsStart, TStart> | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) | | [http](./kibana-plugin-core-public.coresetup.http.md) | HttpSetup | [HttpSetup](./kibana-plugin-core-public.httpsetup.md) | diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.doc_link_version.md b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.doc_link_version.md new file mode 100644 index 00000000000000..c8d13bab92b058 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.doc_link_version.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) > [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinkssetup.doc_link_version.md) + +## DocLinksSetup.DOC\_LINK\_VERSION property + +Signature: + +```typescript +readonly DOC_LINK_VERSION: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.elastic_website_url.md b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.elastic_website_url.md new file mode 100644 index 00000000000000..d8493148bae107 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.elastic_website_url.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) > [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinkssetup.elastic_website_url.md) + +## DocLinksSetup.ELASTIC\_WEBSITE\_URL property + +Signature: + +```typescript +readonly ELASTIC_WEBSITE_URL: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.links.md similarity index 94% rename from docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md rename to docs/development/core/public/kibana-plugin-core-public.doclinkssetup.links.md index b43c484f8de7f9..fd05ae139ba215 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.links.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) > [links](./kibana-plugin-core-public.doclinksstart.links.md) +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) > [links](./kibana-plugin-core-public.doclinkssetup.links.md) -## DocLinksStart.links property +## DocLinksSetup.links property Signature: diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.md b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.md new file mode 100644 index 00000000000000..1114e05589c4b7 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.doclinkssetup.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) + +## DocLinksSetup interface + + +Signature: + +```typescript +export interface DocLinksSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinkssetup.doc_link_version.md) | string | | +| [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinkssetup.elastic_website_url.md) | string | | +| [links](./kibana-plugin-core-public.doclinkssetup.links.md) | {
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
} | | + diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.doc_link_version.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.doc_link_version.md deleted file mode 100644 index 8140b3fcf380f9..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.doc_link_version.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) > [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) - -## DocLinksStart.DOC\_LINK\_VERSION property - -Signature: - -```typescript -readonly DOC_LINK_VERSION: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.elastic_website_url.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.elastic_website_url.md deleted file mode 100644 index af770ed3055aad..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.elastic_website_url.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) > [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) - -## DocLinksStart.ELASTIC\_WEBSITE\_URL property - -Signature: - -```typescript -readonly ELASTIC_WEBSITE_URL: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 69bc4e473590c8..af2a41b691727d 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -2,20 +2,11 @@ [Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) -## DocLinksStart interface +## DocLinksStart type Signature: ```typescript -export interface DocLinksStart +export declare type DocLinksStart = DocLinksSetup; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | -| [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
} | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index eafc81447ee03e..b2524ec48c757d 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -65,7 +65,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ContextSetup](./kibana-plugin-core-public.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CoreSetup](./kibana-plugin-core-public.coresetup.md) | Core services exposed to the Plugin setup lifecycle | | [CoreStart](./kibana-plugin-core-public.corestart.md) | Core services exposed to the Plugin start lifecycle | -| [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | | +| [DocLinksSetup](./kibana-plugin-core-public.doclinkssetup.md) | | | [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) | | | [ErrorToastOptions](./kibana-plugin-core-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) error APIs. | | [FatalErrorInfo](./kibana-plugin-core-public.fatalerrorinfo.md) | Represents the message and stack of a fatal Error | @@ -149,6 +149,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-core-public.chromehelpextensionmenugithublink.md) | | | [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | | | [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | | +| [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | | | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | | [Freezable](./kibana-plugin-core-public.freezable.md) | | | [HandlerContextType](./kibana-plugin-core-public.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md) to represent the type of the context. | diff --git a/docs/user/monitoring/configuring-monitoring.asciidoc b/docs/user/monitoring/configuring-monitoring.asciidoc index de9e99117fc990..776a76b24c4ec3 100644 --- a/docs/user/monitoring/configuring-monitoring.asciidoc +++ b/docs/user/monitoring/configuring-monitoring.asciidoc @@ -8,8 +8,8 @@ If you enable the {monitor-features} in your cluster, there are two methods to collect metrics about {kib}: -* <> -* <> +* <> +* <> You can also use {kib} to <>. @@ -17,6 +17,6 @@ You can also use {kib} to To learn about monitoring in general, see {ref}/monitor-elasticsearch-cluster.html[Monitor a cluster]. -include::monitoring-kibana.asciidoc[] include::monitoring-metricbeat.asciidoc[] include::viewing-metrics.asciidoc[] +include::monitoring-kibana.asciidoc[] diff --git a/docs/user/monitoring/monitoring-kibana.asciidoc b/docs/user/monitoring/monitoring-kibana.asciidoc index 9aa10289d299b3..bb619ac79e143e 100644 --- a/docs/user/monitoring/monitoring-kibana.asciidoc +++ b/docs/user/monitoring/monitoring-kibana.asciidoc @@ -1,15 +1,15 @@ [role="xpack"] [[monitoring-kibana]] -=== Collecting {kib} monitoring data +=== Collecting monitoring data using legacy collectors ++++ -Collecting monitoring data +Legacy collection methods ++++ If you enable the Elastic {monitor-features} in your cluster, you can optionally collect metrics about {kib}. The following method involves sending the metrics to the production cluster, -which ultimately routes them to the monitoring cluster. For an alternative +which ultimately routes them to the monitoring cluster. For the recommended method, see <>. To learn about monitoring in general, see diff --git a/examples/alerting_example/package.json b/examples/alerting_example/package.json deleted file mode 100644 index 96187d847c1c41..00000000000000 --- a/examples/alerting_example/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "alerting_example", - "version": "1.0.0", - "main": "target/examples/alerting_example", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/bfetch_explorer/package.json b/examples/bfetch_explorer/package.json deleted file mode 100644 index ea5a1b1848613b..00000000000000 --- a/examples/bfetch_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "bfetch_explorer", - "version": "1.0.0", - "main": "target/examples/bfetch_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/demo_search/package.json b/examples/demo_search/package.json deleted file mode 100644 index d29ad55b32db2d..00000000000000 --- a/examples/demo_search/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "demo_data_search", - "version": "1.0.0", - "main": "target/examples/demo_data_search", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/embeddable_examples/package.json b/examples/embeddable_examples/package.json deleted file mode 100644 index 055ee6d7315123..00000000000000 --- a/examples/embeddable_examples/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "embeddable_examples", - "version": "1.0.0", - "main": "target/examples/embeddable_examples", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/embeddable_explorer/package.json b/examples/embeddable_explorer/package.json deleted file mode 100644 index 771c5c9be9c06e..00000000000000 --- a/examples/embeddable_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "embeddable_explorer", - "version": "1.0.0", - "main": "target/examples/embeddable_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/search_explorer/package.json b/examples/search_explorer/package.json deleted file mode 100644 index f3c71c5d181f8d..00000000000000 --- a/examples/search_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "searchExplorer", - "version": "1.0.0", - "main": "target/examples/search_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/state_containers_examples/package.json b/examples/state_containers_examples/package.json deleted file mode 100644 index b309494a366628..00000000000000 --- a/examples/state_containers_examples/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "state_containers_examples", - "version": "1.0.0", - "main": "target/examples/state_containers_examples", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/ui_action_examples/package.json b/examples/ui_action_examples/package.json deleted file mode 100644 index fbede6b4dbad7d..00000000000000 --- a/examples/ui_action_examples/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "ui_actions_examples", - "version": "1.0.0", - "main": "target/examples/ui_actions_examples", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/ui_actions_explorer/package.json b/examples/ui_actions_explorer/package.json deleted file mode 100644 index 1758471ced483a..00000000000000 --- a/examples/ui_actions_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "ui_actions_explorer", - "version": "1.0.0", - "main": "target/examples/ui_actions_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/url_generators_examples/package.json b/examples/url_generators_examples/package.json deleted file mode 100644 index d3e8bd98aebf4c..00000000000000 --- a/examples/url_generators_examples/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "url_generators_examples", - "version": "1.0.0", - "main": "target/examples/url_generators_examples", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/examples/url_generators_explorer/package.json b/examples/url_generators_explorer/package.json deleted file mode 100644 index f472ba1d08506a..00000000000000 --- a/examples/url_generators_explorer/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "url_generators_explorer", - "version": "1.0.0", - "main": "target/examples/url_generators_explorer", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "scripts": { - "kbn": "node ../../scripts/kbn.js", - "build": "rm -rf './target' && tsc" - }, - "devDependencies": { - "typescript": "3.7.2" - } -} diff --git a/package.json b/package.json index 810d9ddb7e3378..72a63ec34d2c76 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "kbn:watch": "node scripts/kibana --dev --logging.json=false", "build:types": "tsc --p tsconfig.types.json", "docs:acceptApiChanges": "node --max-old-space-size=6144 scripts/check_published_api_changes.js --accept", - "kbn:bootstrap": "yarn build:types && node scripts/register_git_hook", + "kbn:bootstrap": "node scripts/register_git_hook", "spec_to_console": "node scripts/spec_to_console", "backport-skip-ci": "backport --prDescription \"[skip-ci]\"", "storybook": "node scripts/storybook", @@ -298,7 +298,7 @@ "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^5.0.1", - "@elastic/static-fs": "1.0.1", + "@elastic/static-fs": "1.0.2", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/eslint-import-resolver-kibana": "2.0.0", diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js index 577be820ccc7bc..e16ba0d16bb876 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js @@ -196,6 +196,28 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { ], invalid: [ + { + code: 'export { b } from "../server/b.js"', + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), + options: [ + { + basePath: __dirname, + zones: [ + { + target: 'files/no_restricted_paths/client/**/*', + from: 'files/no_restricted_paths/server/**/*', + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "../server/b.js" imported in restricted zone.', + line: 1, + column: 19, + }, + ], + }, { code: 'import b from "../server/b.js"', filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js index bed10e77597b78..40c53c7f28f6fd 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -126,6 +126,10 @@ module.exports = { } return { + ExportNamedDeclaration(node) { + if (!node.source) return; + checkForRestrictedImportPath(node.source.value, node.source); + }, ImportDeclaration(node) { checkForRestrictedImportPath(node.source.value, node.source); }, diff --git a/packages/kbn-plugin-helpers/bin/plugin-helpers.js b/packages/kbn-plugin-helpers/bin/plugin-helpers.js index 18a08332605ea5..175ff1019fa2dc 100755 --- a/packages/kbn-plugin-helpers/bin/plugin-helpers.js +++ b/packages/kbn-plugin-helpers/bin/plugin-helpers.js @@ -25,4 +25,4 @@ if (nodeMajorVersion < 6) { process.exit(1); } -require('../cli'); +require('../target/cli'); diff --git a/packages/kbn-plugin-helpers/lib/run.test.js b/packages/kbn-plugin-helpers/lib/run.test.js deleted file mode 100644 index 4c87399319b3bb..00000000000000 --- a/packages/kbn-plugin-helpers/lib/run.test.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*eslint-env jest*/ - -jest.mock('./plugin_config', () => () => ({ id: 'testPlugin' })); - -jest.mock('./tasks', () => { - return { testTask: jest.fn() }; -}); - -const run = require('./run'); - -describe('lib/run', () => { - beforeEach(() => jest.resetAllMocks()); - - it('throw given an invalid task', function() { - const invalidTaskName = 'thisisnotavalidtasknameandneverwillbe'; - const runner = () => run(invalidTaskName); - - expect(runner).toThrow(/invalid task/i); - }); - - it('runs specified task with plugin and runner', function() { - run('testTask'); - - const { testTask } = require('./tasks'); - const plugin = require('./plugin_config')(); - const args = testTask.mock.calls[0]; - expect(testTask.mock.calls).toHaveLength(1); - expect(args[0]).toEqual(plugin); - expect(args[1]).toBe(run); - }); - - it('returns value returned by task', function() { - const { testTask } = require('./tasks'); - - const symbol = Symbol('foo'); - testTask.mockReturnValue(symbol); - expect(run('testTask')).toBe(symbol); - }); -}); diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 53c998ddfaac24..040b779a699519 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -1,17 +1,16 @@ { "name": "@kbn/plugin-helpers", - "private": true, "version": "9.0.2", + "private": true, "description": "Just some helpers for kibana plugin devs.", - "main": "lib/index.js", + "license": "Apache-2.0", + "main": "target/lib/index.js", + "scripts": { + "kbn:bootstrap": "tsc" + }, "bin": { "plugin-helpers": "bin/plugin-helpers.js" }, - "author": "Spencer Alger ", - "license": "Apache-2.0", - "peerDependencies": { - "@kbn/babel-preset": "1.0.0" - }, "dependencies": { "@babel/core": "^7.9.0", "argv-split": "^2.0.1", @@ -27,6 +26,20 @@ "node-sass": "^4.13.1", "through2": "^2.0.3", "through2-map": "^3.0.0", + "vinyl": "^2.2.0", "vinyl-fs": "^3.0.3" + }, + "devDependencies": { + "@types/gulp-rename": "^0.0.33", + "@types/gulp-zip": "^4.0.1", + "@types/inquirer": "^6.5.0", + "@types/node-sass": "^4.11.0", + "@types/through2": "^2.0.35", + "@types/through2-map": "^3.0.0", + "@types/vinyl": "^2.0.4", + "typescript": "3.7.2" + }, + "peerDependencies": { + "@kbn/babel-preset": "1.0.0" } } diff --git a/packages/kbn-plugin-helpers/cli.js b/packages/kbn-plugin-helpers/src/cli.ts similarity index 87% rename from packages/kbn-plugin-helpers/cli.js rename to packages/kbn-plugin-helpers/src/cli.ts index 48b70535272fe8..ee1bca0fe3ac23 100644 --- a/packages/kbn-plugin-helpers/cli.js +++ b/packages/kbn-plugin-helpers/src/cli.ts @@ -17,13 +17,16 @@ * under the License. */ -const program = require('commander'); +import Fs from 'fs'; +import Path from 'path'; -const pkg = require('./package.json'); -const createCommanderAction = require('./lib/commander_action'); -const docs = require('./lib/docs'); -const enableCollectingUnknownOptions = require('./lib/enable_collecting_unknown_options'); +import program from 'commander'; +import { createCommanderAction } from './lib/commander_action'; +import { docs } from './lib/docs'; +import { enableCollectingUnknownOptions } from './lib/enable_collecting_unknown_options'; + +const pkg = JSON.parse(Fs.readFileSync(Path.resolve(__dirname, '../package.json'), 'utf8')); program.version(pkg.version); enableCollectingUnknownOptions( @@ -55,7 +58,7 @@ program buildVersion: command.buildVersion, kibanaVersion: command.kibanaVersion, skipArchive: Boolean(command.skipArchive), - files: files, + files, })) ); @@ -84,7 +87,7 @@ program .on('--help', docs('test/mocha')) .action( createCommanderAction('testMocha', (command, files) => ({ - files: files, + files, })) ); diff --git a/packages/kbn-plugin-helpers/lib/__snapshots__/commander_action.test.js.snap b/packages/kbn-plugin-helpers/src/lib/__snapshots__/commander_action.test.js.snap similarity index 100% rename from packages/kbn-plugin-helpers/lib/__snapshots__/commander_action.test.js.snap rename to packages/kbn-plugin-helpers/src/lib/__snapshots__/commander_action.test.js.snap diff --git a/packages/kbn-plugin-helpers/lib/commander_action.test.js b/packages/kbn-plugin-helpers/src/lib/commander_action.test.js similarity index 94% rename from packages/kbn-plugin-helpers/lib/commander_action.test.js rename to packages/kbn-plugin-helpers/src/lib/commander_action.test.js index 40ac6755f02e54..501c0bd0619130 100644 --- a/packages/kbn-plugin-helpers/lib/commander_action.test.js +++ b/packages/kbn-plugin-helpers/src/lib/commander_action.test.js @@ -17,12 +17,10 @@ * under the License. */ -/*eslint-env jest*/ +import { createCommanderAction } from './commander_action'; +import { run } from './run'; -const createCommanderAction = require('./commander_action'); -const run = require('./run'); - -jest.mock('./run', () => jest.fn()); +jest.mock('./run'); const STACK_TRACE_RE = /\n(?:\s+at .+(?:\n|$))+/g; expect.addSnapshotSerializer({ diff --git a/packages/kbn-plugin-helpers/lib/commander_action.js b/packages/kbn-plugin-helpers/src/lib/commander_action.ts similarity index 81% rename from packages/kbn-plugin-helpers/lib/commander_action.js rename to packages/kbn-plugin-helpers/src/lib/commander_action.ts index 497dee8edf740d..ce585dd0a02c88 100644 --- a/packages/kbn-plugin-helpers/lib/commander_action.js +++ b/packages/kbn-plugin-helpers/src/lib/commander_action.ts @@ -17,10 +17,13 @@ * under the License. */ -const run = require('./run'); +import { run } from './run'; +import { Tasks } from './tasks'; -module.exports = function createCommanderAction(taskName, getOptions = () => {}) { - return async (...args) => { +type GetOptions = (command: any, ...args: string[]) => any; + +export function createCommanderAction(taskName: keyof Tasks, getOptions: GetOptions = () => {}) { + return async (...args: string[]) => { try { // command is the last arg passed by commander, but we move it to the front of the list const command = args.pop(); @@ -30,4 +33,4 @@ module.exports = function createCommanderAction(taskName, getOptions = () => {}) process.exit(1); } }; -}; +} diff --git a/packages/kbn-plugin-helpers/lib/config_file.js b/packages/kbn-plugin-helpers/src/lib/config_file.ts similarity index 70% rename from packages/kbn-plugin-helpers/lib/config_file.js rename to packages/kbn-plugin-helpers/src/lib/config_file.ts index 5b8be836998e44..1566932b658722 100644 --- a/packages/kbn-plugin-helpers/lib/config_file.js +++ b/packages/kbn-plugin-helpers/src/lib/config_file.ts @@ -17,33 +17,34 @@ * under the License. */ -const resolve = require('path').resolve; -const readFileSync = require('fs').readFileSync; +import { resolve } from 'path'; +import { readFileSync } from 'fs'; -const configFiles = ['.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json']; -const configCache = {}; +const configFileNames = ['.kibana-plugin-helpers.json', '.kibana-plugin-helpers.dev.json']; -module.exports = function(root) { - if (!root) root = process.cwd(); +interface Config { + [key: string]: unknown; +} - if (configCache[root]) { - return configCache[root]; +const configCache = new Map(); + +export function configFile(root: string = process.cwd()) { + if (configCache.has(root)) { + return configCache.get(root)!; } // config files to read from, in the order they are merged together - let config = (configCache[root] = {}); - - configFiles.forEach(function(configFile) { + let config: Config = {}; + for (const name of configFileNames) { try { - const content = JSON.parse(readFileSync(resolve(root, configFile))); - config = Object.assign(config, content); + config = JSON.parse(readFileSync(resolve(root, name), 'utf8')); } catch (e) { // rethrow error unless it's complaining about file not existing if (e.code !== 'ENOENT') { throw e; } } - }); + } const deprecationMsg = 'has been removed from `@kbn/plugin-helpers`. ' + @@ -59,8 +60,10 @@ module.exports = function(root) { } // use resolve to ensure correct resolution of paths - const { includePlugins } = config; - if (includePlugins) config.includePlugins = includePlugins.map(path => resolve(root, path)); + if (Array.isArray(config.includePlugins)) { + config.includePlugins = config.includePlugins.map((path: string) => resolve(root, path)); + } + configCache.set(root, config); return config; -}; +} diff --git a/packages/kbn-plugin-helpers/lib/docs.js b/packages/kbn-plugin-helpers/src/lib/docs.ts similarity index 72% rename from packages/kbn-plugin-helpers/lib/docs.js rename to packages/kbn-plugin-helpers/src/lib/docs.ts index f1b288c68c390e..68c095209e8170 100644 --- a/packages/kbn-plugin-helpers/lib/docs.js +++ b/packages/kbn-plugin-helpers/src/lib/docs.ts @@ -17,21 +17,19 @@ * under the License. */ -const resolve = require('path').resolve; -const readFileSync = require('fs').readFileSync; +import { resolve } from 'path'; +import { readFileSync } from 'fs'; -function indent(txt, n) { +function indent(txt: string, n: number) { const space = new Array(n + 1).join(' '); return space + txt.split('\n').join('\n' + space); } -module.exports = function docs(name) { - const md = readFileSync(resolve(__dirname, '../tasks', name, 'README.md'), 'utf8'); +export function docs(name: string) { + const md = readFileSync(resolve(__dirname, '../../src/tasks', name, 'README.md'), 'utf8'); return function() { - console.log('\n Docs:'); - console.log(''); - console.log(indent(md, 4)); - console.log(''); + /* eslint-disable-next-line no-console */ + console.log(`\n Docs:\n\n${indent(md, 4)}\n\n`); }; -}; +} diff --git a/packages/kbn-plugin-helpers/lib/enable_collecting_unknown_options.js b/packages/kbn-plugin-helpers/src/lib/enable_collecting_unknown_options.ts similarity index 86% rename from packages/kbn-plugin-helpers/lib/enable_collecting_unknown_options.js rename to packages/kbn-plugin-helpers/src/lib/enable_collecting_unknown_options.ts index 8b855a8b8769d2..77fa7f2fcae846 100644 --- a/packages/kbn-plugin-helpers/lib/enable_collecting_unknown_options.js +++ b/packages/kbn-plugin-helpers/src/lib/enable_collecting_unknown_options.ts @@ -17,12 +17,14 @@ * under the License. */ -module.exports = function enableCollectingUnknownOptions(command) { +import { Command } from 'commander'; + +export function enableCollectingUnknownOptions(command: Command) { const origParse = command.parseOptions; command.allowUnknownOption(); - command.parseOptions = function(argv) { + command.parseOptions = function(argv: string[]) { const opts = origParse.call(this, argv); this.unknownOptions = opts.unknown; return opts; }; -}; +} diff --git a/packages/kbn-plugin-helpers/src/lib/index.ts b/packages/kbn-plugin-helpers/src/lib/index.ts new file mode 100644 index 00000000000000..acc7134308bec3 --- /dev/null +++ b/packages/kbn-plugin-helpers/src/lib/index.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './run'; +export * from './utils'; +export * from './win_cmd'; +export * from './plugin_config'; +export * from './pipeline'; diff --git a/packages/kbn-plugin-helpers/src/lib/pipeline.ts b/packages/kbn-plugin-helpers/src/lib/pipeline.ts new file mode 100644 index 00000000000000..d66e18f5239a90 --- /dev/null +++ b/packages/kbn-plugin-helpers/src/lib/pipeline.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Stream from 'stream'; +import Util from 'util'; + +export const pipeline = Util.promisify(Stream.pipeline); diff --git a/packages/kbn-plugin-helpers/lib/plugin_config.js b/packages/kbn-plugin-helpers/src/lib/plugin_config.ts similarity index 64% rename from packages/kbn-plugin-helpers/lib/plugin_config.js rename to packages/kbn-plugin-helpers/src/lib/plugin_config.ts index 60baa7fc976607..dc3ef936e2164e 100644 --- a/packages/kbn-plugin-helpers/lib/plugin_config.js +++ b/packages/kbn-plugin-helpers/src/lib/plugin_config.ts @@ -17,16 +17,26 @@ * under the License. */ -const resolve = require('path').resolve; -const readFileSync = require('fs').readFileSync; -const configFile = require('./config_file'); +import { resolve } from 'path'; +import { readFileSync } from 'fs'; -module.exports = function(root) { - if (!root) root = process.cwd(); +import { configFile } from './config_file'; +export interface PluginConfig { + root: string; + kibanaRoot: string; + serverTestPatterns: string[]; + buildSourcePatterns: string[]; + skipInstallDependencies: boolean; + id: string; + version: string; + pkg: any; + [k: string]: unknown; +} + +export function pluginConfig(root: string = process.cwd()): PluginConfig { const pluginPackageJsonPath = resolve(root, 'package.json'); - const pkg = JSON.parse(readFileSync(pluginPackageJsonPath)); - const config = configFile(root); + const pkg = JSON.parse(readFileSync(pluginPackageJsonPath, 'utf8')); const buildSourcePatterns = [ 'yarn.lock', @@ -42,6 +52,7 @@ module.exports = function(root) { const isPluginXpack = pkg.name === 'x-pack'; if (isPluginOnKibanaExtra && !isPluginXpack) { + // eslint-disable-next-line no-console console.warn( `In the future we will disable ../kibana-extra/{pluginName}. You should move your plugin ${pkg.name} as soon as possible to ./plugins/{pluginName}` ); @@ -49,17 +60,15 @@ module.exports = function(root) { const kibanaRootWhenNotXpackPlugin = isPluginOnKibanaExtra ? kibanaExtraDir : kibanaPluginsDir; - return Object.assign( - { - root: root, - kibanaRoot: isPluginXpack ? resolve(root, '..') : kibanaRootWhenNotXpackPlugin, - serverTestPatterns: ['server/**/__tests__/**/*.js'], - buildSourcePatterns: buildSourcePatterns, - skipInstallDependencies: false, - id: pkg.name, - pkg: pkg, - version: pkg.version, - }, - config - ); -}; + return { + root, + kibanaRoot: isPluginXpack ? resolve(root, '..') : kibanaRootWhenNotXpackPlugin, + serverTestPatterns: ['server/**/__tests__/**/*.js'], + buildSourcePatterns, + skipInstallDependencies: false, + id: pkg.name as string, + version: pkg.version as string, + pkg, + ...configFile(root), + }; +} diff --git a/packages/kbn-plugin-helpers/lib/run.js b/packages/kbn-plugin-helpers/src/lib/run.ts similarity index 75% rename from packages/kbn-plugin-helpers/lib/run.js rename to packages/kbn-plugin-helpers/src/lib/run.ts index 119b64f71e2f95..2b1a2a63c1074e 100644 --- a/packages/kbn-plugin-helpers/lib/run.js +++ b/packages/kbn-plugin-helpers/src/lib/run.ts @@ -17,15 +17,21 @@ * under the License. */ -const pluginConfig = require('./plugin_config'); -const tasks = require('./tasks'); +import { pluginConfig, PluginConfig } from './plugin_config'; +import { tasks, Tasks } from './tasks'; -module.exports = function run(name, options) { +export interface TaskContext { + plugin: PluginConfig; + run: typeof run; + options?: any; +} + +export function run(name: keyof Tasks, options?: any) { const action = tasks[name]; if (!action) { throw new Error('Invalid task: "' + name + '"'); } const plugin = pluginConfig(); - return action(plugin, run, options); -}; + return action({ plugin, run, options }); +} diff --git a/packages/kbn-plugin-helpers/lib/tasks.js b/packages/kbn-plugin-helpers/src/lib/tasks.ts similarity index 64% rename from packages/kbn-plugin-helpers/lib/tasks.js rename to packages/kbn-plugin-helpers/src/lib/tasks.ts index afc9c056d51d70..7817838760a2e1 100644 --- a/packages/kbn-plugin-helpers/lib/tasks.js +++ b/packages/kbn-plugin-helpers/src/lib/tasks.ts @@ -17,13 +17,22 @@ * under the License. */ -const buildTask = require('../tasks/build'); -const startTask = require('../tasks/start'); -const testAllTask = require('../tasks/test/all'); -const testKarmaTask = require('../tasks/test/karma'); -const testMochaTask = require('../tasks/test/mocha'); +import { buildTask } from '../tasks/build'; +import { startTask } from '../tasks/start'; +import { testAllTask } from '../tasks/test/all'; +import { testKarmaTask } from '../tasks/test/karma'; +import { testMochaTask } from '../tasks/test/mocha'; -module.exports = { +// define a tasks interface that we can extend in the tests +export interface Tasks { + build: typeof buildTask; + start: typeof startTask; + testAll: typeof testAllTask; + testKarma: typeof testKarmaTask; + testMocha: typeof testMochaTask; +} + +export const tasks: Tasks = { build: buildTask, start: startTask, testAll: testAllTask, diff --git a/packages/kbn-plugin-helpers/lib/utils.js b/packages/kbn-plugin-helpers/src/lib/utils.ts similarity index 74% rename from packages/kbn-plugin-helpers/lib/utils.js rename to packages/kbn-plugin-helpers/src/lib/utils.ts index 6e3d8969802a45..0348f9f8deda98 100644 --- a/packages/kbn-plugin-helpers/lib/utils.js +++ b/packages/kbn-plugin-helpers/src/lib/utils.ts @@ -17,11 +17,11 @@ * under the License. */ -const resolve = require('path').resolve; +import { resolve } from 'path'; -const pluginConfig = require('./plugin_config'); +import { pluginConfig } from './plugin_config'; -function babelRegister() { +export function babelRegister() { const plugin = pluginConfig(); try { @@ -36,18 +36,7 @@ function babelRegister() { } } -function resolveKibanaPath(path) { +export function resolveKibanaPath(path: string) { const plugin = pluginConfig(); return resolve(plugin.kibanaRoot, path); } - -function readFtrConfigFile(log, path, settingOverrides) { - return require('@kbn/test') // eslint-disable-line import/no-dynamic-require - .readConfigFile(log, path, settingOverrides); -} - -module.exports = { - babelRegister: babelRegister, - resolveKibanaPath: resolveKibanaPath, - readFtrConfigFile: readFtrConfigFile, -}; diff --git a/packages/kbn-plugin-helpers/src/lib/win_cmd.ts b/packages/kbn-plugin-helpers/src/lib/win_cmd.ts new file mode 100644 index 00000000000000..a3f5d144602ed5 --- /dev/null +++ b/packages/kbn-plugin-helpers/src/lib/win_cmd.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Os from 'os'; + +export function winCmd(cmd: string) { + return /^win/.test(Os.platform()) ? cmd + '.cmd' : cmd; +} diff --git a/packages/kbn-plugin-helpers/tasks/build/README.md b/packages/kbn-plugin-helpers/src/tasks/build/README.md similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/README.md rename to packages/kbn-plugin-helpers/src/tasks/build/README.md diff --git a/packages/kbn-plugin-helpers/tasks/build/build_action.js b/packages/kbn-plugin-helpers/src/tasks/build/build_task.ts similarity index 57% rename from packages/kbn-plugin-helpers/tasks/build/build_action.js rename to packages/kbn-plugin-helpers/src/tasks/build/build_task.ts index 32513958f66abb..16207f323ff71b 100644 --- a/packages/kbn-plugin-helpers/tasks/build/build_action.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/build_task.ts @@ -17,15 +17,14 @@ * under the License. */ -const join = require('path').join; -const resolve = require('path').resolve; -const inquirer = require('inquirer'); +import { join, resolve } from 'path'; +import inquirer from 'inquirer'; -const createBuild = require('./create_build'); -const createPackage = require('./create_package'); +import { TaskContext } from '../../lib'; +import { createBuild } from './create_build'; +import { createPackage } from './create_package'; -module.exports = function(plugin, run, options) { - options = options || {}; +export async function buildTask({ plugin, options = {} }: TaskContext) { let buildVersion = plugin.version; let kibanaVersion = (plugin.pkg.kibana && plugin.pkg.kibana.version) || plugin.pkg.version; let buildFiles = plugin.buildSourcePatterns; @@ -41,31 +40,24 @@ module.exports = function(plugin, run, options) { if (options.buildVersion) buildVersion = options.buildVersion; if (options.kibanaVersion) kibanaVersion = options.kibanaVersion; - let buildStep; - if (kibanaVersion === 'kibana') { - buildStep = askForKibanaVersion().then(function(customKibanaVersion) { - return createBuild(plugin, buildTarget, buildVersion, customKibanaVersion, buildFiles); - }); - } else { - buildStep = createBuild(plugin, buildTarget, buildVersion, kibanaVersion, buildFiles); + const chosenKibanaVersion = + kibanaVersion === 'kibana' ? await askForKibanaVersion() : kibanaVersion; + + await createBuild(plugin, buildTarget, buildVersion, chosenKibanaVersion, buildFiles); + + if (!options.skipArchive) { + await createPackage(plugin, buildTarget, buildVersion); } +} - return buildStep.then(function() { - if (options.skipArchive) return; - return createPackage(plugin, buildTarget, buildVersion); - }); -}; +async function askForKibanaVersion() { + const answers = await inquirer.prompt([ + { + type: 'input', + name: 'kibanaVersion', + message: 'What version of Kibana are you building for?', + }, + ]); -function askForKibanaVersion() { - return inquirer - .prompt([ - { - type: 'input', - name: 'kibanaVersion', - message: 'What version of Kibana are you building for?', - }, - ]) - .then(function(answers) { - return answers.kibanaVersion; - }); + return answers.kibanaVersion; } diff --git a/packages/kbn-plugin-helpers/src/tasks/build/create_build.ts b/packages/kbn-plugin-helpers/src/tasks/build/create_build.ts new file mode 100644 index 00000000000000..a469e4fe673908 --- /dev/null +++ b/packages/kbn-plugin-helpers/src/tasks/build/create_build.ts @@ -0,0 +1,196 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { relative } from 'path'; +import path from 'path'; + +import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs'; +import execa from 'execa'; +import sass from 'node-sass'; +import del from 'del'; +import File from 'vinyl'; +import vfs from 'vinyl-fs'; +import rename from 'gulp-rename'; +import through from 'through2'; +import minimatch from 'minimatch'; +// @ts-ignore +import gulpBabel from 'gulp-babel'; + +import { PluginConfig, winCmd, pipeline } from '../../lib'; +import { rewritePackageJson } from './rewrite_package_json'; + +// `link:` dependencies create symlinks, but we don't want to include symlinks +// in the built zip file. Therefore we remove all symlinked dependencies, so we +// can re-create them when installing the plugin. +function removeSymlinkDependencies(root: string) { + const nodeModulesPattern = path.join(root, '**', 'node_modules', '**'); + + return through.obj((file: File, _, cb) => { + const isSymlink = file.symlink != null; + const isDependency = minimatch(file.path, nodeModulesPattern); + + if (isSymlink && isDependency) { + unlinkSync(file.path); + } + + cb(); + }); +} + +// parse a ts config file +function parseTsconfig(pluginSourcePath: string, configPath: string) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const ts = require(path.join(pluginSourcePath, 'node_modules', 'typescript')); + + const { error, config } = ts.parseConfigFileTextToJson( + configPath, + readFileSync(configPath, 'utf8') + ); + + if (error) { + throw error; + } + + return config; +} + +// transpile with babel +async function transpileWithBabel(srcGlobs: string[], buildRoot: string, presets: string[]) { + await pipeline( + vfs.src( + srcGlobs.concat([ + '!**/*.d.ts', + '!**/*.{test,test.mocks,mock,mocks}.{ts,tsx}', + '!**/node_modules/**', + '!**/bower_components/**', + '!**/__tests__/**', + ]), + { + cwd: buildRoot, + } + ), + + gulpBabel({ + babelrc: false, + presets, + }), + + vfs.dest(buildRoot) + ); +} + +export async function createBuild( + plugin: PluginConfig, + buildTarget: string, + buildVersion: string, + kibanaVersion: string, + files: string[] +) { + const buildSource = plugin.root; + const buildRoot = path.join(buildTarget, 'kibana', plugin.id); + + await del(buildTarget); + + // copy source files and apply some transformations in the process + await pipeline( + vfs.src(files, { + cwd: buildSource, + base: buildSource, + allowEmpty: true, + }), + + // modify the package.json file + rewritePackageJson(buildSource, buildVersion, kibanaVersion), + + // put all files inside the correct directories + rename(function nestFileInDir(filePath) { + const nonRelativeDirname = filePath.dirname!.replace(/^(\.\.\/?)+/g, ''); + filePath.dirname = path.join(relative(buildTarget, buildRoot), nonRelativeDirname); + }), + + // write files back to disk + vfs.dest(buildTarget) + ); + + // install packages in build + if (!plugin.skipInstallDependencies) { + execa.sync(winCmd('yarn'), ['install', '--production', '--pure-lockfile'], { + cwd: buildRoot, + }); + } + + // compile stylesheet + if (typeof plugin.styleSheetToCompile === 'string') { + const file = path.resolve(plugin.root, plugin.styleSheetToCompile); + if (!existsSync(file)) { + throw new Error(`Path provided for styleSheetToCompile does not exist: ${file}`); + } + + const outputFileName = path.basename(file, path.extname(file)) + '.css'; + const output = path.join(buildRoot, path.dirname(plugin.styleSheetToCompile), outputFileName); + + const rendered = sass.renderSync({ file, output }); + writeFileSync(output, rendered.css); + + del.sync([path.join(buildRoot, '**', '*.s{a,c}ss')]); + } + + // transform typescript to js and clean out typescript + const tsConfigPath = path.join(buildRoot, 'tsconfig.json'); + if (existsSync(tsConfigPath)) { + // attempt to patch the extends path in the tsconfig file + const buildConfig = parseTsconfig(buildSource, tsConfigPath); + + if (buildConfig.extends) { + buildConfig.extends = path.join(relative(buildRoot, buildSource), buildConfig.extends); + + writeFileSync(tsConfigPath, JSON.stringify(buildConfig)); + } + + // Transpile ts server code + // + // Include everything except content from public folders + await transpileWithBabel(['**/*.{ts,tsx}', '!**/public/**'], buildRoot, [ + require.resolve('@kbn/babel-preset/node_preset'), + ]); + + // Transpile ts client code + // + // Include everything inside a public directory + await transpileWithBabel(['**/public/**/*.{ts,tsx}'], buildRoot, [ + require.resolve('@kbn/babel-preset/webpack_preset'), + ]); + + del.sync([ + path.join(buildRoot, '**', '*.{ts,tsx,d.ts}'), + path.join(buildRoot, 'tsconfig.json'), + ]); + } + + // remove symlinked dependencies + await pipeline( + vfs.src([relative(buildTarget, buildRoot) + '/**/*'], { + cwd: buildTarget, + base: buildTarget, + resolveSymlinks: false, + }), + + removeSymlinkDependencies(buildRoot) + ); +} diff --git a/packages/kbn-plugin-helpers/tasks/build/create_package.js b/packages/kbn-plugin-helpers/src/tasks/build/create_package.ts similarity index 58% rename from packages/kbn-plugin-helpers/tasks/build/create_package.js rename to packages/kbn-plugin-helpers/src/tasks/build/create_package.ts index 6df55fda902b04..9fa2305a94eabc 100644 --- a/packages/kbn-plugin-helpers/tasks/build/create_package.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/create_package.ts @@ -17,28 +17,30 @@ * under the License. */ -const join = require('path').join; -const relative = require('path').relative; -const del = require('del'); -const vfs = require('vinyl-fs'); -const zip = require('gulp-zip'); +import { relative, join } from 'path'; -module.exports = function createPackage(plugin, buildTarget, buildVersion) { +import del from 'del'; +import vfs from 'vinyl-fs'; +import zip from 'gulp-zip'; + +import { pipeline, PluginConfig } from '../../lib'; + +export async function createPackage( + plugin: PluginConfig, + buildTarget: string, + buildVersion: string +) { const buildId = `${plugin.id}-${buildVersion}`; const buildRoot = join(buildTarget, 'kibana', plugin.id); + const buildFiles = [relative(buildTarget, buildRoot) + '/**/*']; // zip up the package - return new Promise(function(resolve, reject) { - const buildFiles = [relative(buildTarget, buildRoot) + '/**/*']; + await pipeline( + vfs.src(buildFiles, { cwd: buildTarget, base: buildTarget }), + zip(`${buildId}.zip`), + vfs.dest(buildTarget) + ); - vfs - .src(buildFiles, { cwd: buildTarget, base: buildTarget }) - .pipe(zip(`${buildId}.zip`)) - .pipe(vfs.dest(buildTarget)) - .on('end', resolve) - .on('error', reject); - }).then(function() { - // clean up the build path - return del(join(buildTarget, 'kibana')); - }); -}; + // clean up the build path + await del(join(buildTarget, 'kibana')); +} diff --git a/packages/kbn-plugin-helpers/tasks/build/git_info.js b/packages/kbn-plugin-helpers/src/tasks/build/git_info.ts similarity index 92% rename from packages/kbn-plugin-helpers/tasks/build/git_info.js rename to packages/kbn-plugin-helpers/src/tasks/build/git_info.ts index 655b5c95a01025..e3a68f82e48b29 100644 --- a/packages/kbn-plugin-helpers/tasks/build/git_info.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/git_info.ts @@ -17,16 +17,18 @@ * under the License. */ -const execFileSync = require('child_process').execFileSync; +import { execFileSync } from 'child_process'; -module.exports = function gitInfo(rootPath) { +export function gitInfo(rootPath: string) { try { const LOG_SEPARATOR = '||'; + const commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], { cwd: rootPath, stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', }); + const logLine = execFileSync('git', ['log', '--pretty=%h' + LOG_SEPARATOR + '%cD', '-n', '1'], { cwd: rootPath, stdio: ['ignore', 'pipe', 'ignore'], @@ -41,4 +43,4 @@ module.exports = function gitInfo(rootPath) { } catch (e) { return {}; } -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/start/index.js b/packages/kbn-plugin-helpers/src/tasks/build/index.ts similarity index 94% rename from packages/kbn-plugin-helpers/tasks/start/index.js rename to packages/kbn-plugin-helpers/src/tasks/build/index.ts index b76e91fd5f2898..d22d544a954915 100644 --- a/packages/kbn-plugin-helpers/tasks/start/index.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/index.ts @@ -17,4 +17,4 @@ * under the License. */ -module.exports = require('./start_action'); +export * from './build_task'; diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/index.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/index.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/index.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/index.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/package.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/package.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/package.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/package.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/public/hack.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/public/hack.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/public/hack.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/public/hack.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/translations/es.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/translations/es.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/translations/es.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/build_action_test_plugin/translations/es.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/index.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/index.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/index.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/index.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/package.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/package.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/package.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/package.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/hack.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/hack.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/hack.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/hack.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/styles.scss b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/styles.scss similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/styles.scss rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/public/styles.scss diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/translations/es.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/translations/es.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/translations/es.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_build_test_plugin/translations/es.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/index.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/index.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/index.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/index.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/package.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/package.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/package.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/package.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/public/hack.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/public/hack.js similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/public/hack.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/public/hack.js diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/translations/es.json b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/translations/es.json similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/translations/es.json rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__fixtures__/create_package_test_plugin/translations/es.json diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/__snapshots__/build_action.test.js.snap b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__snapshots__/build_action.test.js.snap similarity index 100% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/__snapshots__/build_action.test.js.snap rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/__snapshots__/build_action.test.js.snap diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/build_action.test.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/build_action.test.js similarity index 74% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/build_action.test.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/build_action.test.js index 90deb0c4ac4a86..f596576fe74669 100644 --- a/packages/kbn-plugin-helpers/tasks/build/integration_tests/build_action.test.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/build_action.test.js @@ -17,34 +17,34 @@ * under the License. */ -const resolve = require('path').resolve; -const fs = require('fs'); -const del = require('del'); +import { resolve } from 'path'; +import fs from 'fs'; +import del from 'del'; +import { pluginConfig } from '../../../lib'; const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/build_action_test_plugin'); const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build'); -const PLUGIN = require('../../../lib/plugin_config')(PLUGIN_FIXTURE); -const noop = () => {}; +const plugin = pluginConfig(PLUGIN_FIXTURE); describe('creating build zip', () => { - const buildAction = require('../build_action'); + const { buildTask } = require('../build_task'); beforeEach(() => del(PLUGIN_BUILD_DIR)); afterEach(() => del(PLUGIN_BUILD_DIR)); it('creates a zip in the build directory', async () => { - await buildAction(PLUGIN); + await buildTask({ plugin }); - const buildFile = resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip'); + const buildFile = resolve(PLUGIN_BUILD_DIR, plugin.id + '-' + plugin.version + '.zip'); if (!fs.existsSync(buildFile)) { throw new Error('Build file not found: ' + buildFile); } }); it('skips zip creation based on flag', async () => { - await buildAction(PLUGIN, noop, { skipArchive: true }); + await buildTask({ plugin, options: { skipArchive: true } }); - const buildFile = resolve(PLUGIN_BUILD_DIR, PLUGIN.id + '-' + PLUGIN.version + '.zip'); + const buildFile = resolve(PLUGIN_BUILD_DIR, plugin.id + '-' + plugin.version + '.zip'); if (fs.existsSync(buildFile)) { throw new Error('Build file not found: ' + buildFile); } @@ -53,13 +53,13 @@ describe('creating build zip', () => { describe('calling create_build', () => { let mockBuild; - let buildAction; + let buildTask; beforeEach(() => { jest.resetModules(); - mockBuild = jest.fn(() => Promise.resolve()); - jest.mock('../create_build', () => mockBuild); - buildAction = require('../build_action'); + jest.mock('../create_build'); + ({ createBuild: mockBuild } = require('../create_build')); + ({ buildTask } = require('../build_task')); }); const nameArgs = ([plugin, buildTarget, buildVersion, kibanaVersion, files]) => ({ @@ -76,7 +76,7 @@ describe('calling create_build', () => { kibanaVersion: '4.5.6', }; - await buildAction(PLUGIN, noop, options); + await buildTask({ plugin, options }); expect(mockBuild.mock.calls).toHaveLength(1); @@ -86,12 +86,12 @@ describe('calling create_build', () => { }); it('uses default file list without files option', async () => { - await buildAction(PLUGIN); + await buildTask({ plugin }); expect(mockBuild.mock.calls).toHaveLength(1); const { files } = nameArgs(mockBuild.mock.calls[0]); - PLUGIN.buildSourcePatterns.forEach(file => expect(files).toContain(file)); + plugin.buildSourcePatterns.forEach(file => expect(files).toContain(file)); }); it('uses only files passed in', async () => { @@ -99,7 +99,7 @@ describe('calling create_build', () => { files: ['index.js', 'LICENSE.txt', 'plugins/**/*', '{server,public}/**/*'], }; - await buildAction(PLUGIN, noop, options); + await buildTask({ plugin, options }); expect(mockBuild.mock.calls).toHaveLength(1); @@ -112,6 +112,6 @@ describe('calling create_build', () => { throw new Error('foo bar'); }); - await expect(buildAction(PLUGIN, noop)).rejects.toThrowErrorMatchingSnapshot(); + await expect(buildTask({ plugin })).rejects.toThrowErrorMatchingSnapshot(); }); }); diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/create_build.test.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_build.test.js similarity index 94% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/create_build.test.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_build.test.js index 42ad1c5ae0cfce..8a4a5a55ce237a 100644 --- a/packages/kbn-plugin-helpers/tasks/build/integration_tests/create_build.test.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_build.test.js @@ -17,13 +17,14 @@ * under the License. */ -const { resolve } = require('path'); -const { readdirSync, existsSync, unlinkSync } = require('fs'); -const del = require('del'); -const createBuild = require('../create_build'); +import { resolve } from 'path'; +import { readdirSync, existsSync, unlinkSync } from 'fs'; +import del from 'del'; +import { createBuild } from '../create_build'; +import { pluginConfig } from '../../../lib'; const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/create_build_test_plugin'); -const PLUGIN = require('../../../lib/plugin_config')(PLUGIN_FIXTURE); +const PLUGIN = pluginConfig(PLUGIN_FIXTURE); const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build'); const PLUGIN_BUILD_TARGET = resolve(PLUGIN_BUILD_DIR, 'kibana', PLUGIN.id); diff --git a/packages/kbn-plugin-helpers/tasks/build/integration_tests/create_package.test.js b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_package.test.js similarity index 85% rename from packages/kbn-plugin-helpers/tasks/build/integration_tests/create_package.test.js rename to packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_package.test.js index 9659cfbaa2a304..13de8fa165c291 100644 --- a/packages/kbn-plugin-helpers/tasks/build/integration_tests/create_package.test.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/integration_tests/create_package.test.js @@ -17,14 +17,15 @@ * under the License. */ -const { resolve } = require('path'); -const { statSync } = require('fs'); -const del = require('del'); -const createBuild = require('../create_build'); -const createPackage = require('../create_package'); +import { resolve } from 'path'; +import { statSync } from 'fs'; +import del from 'del'; +import { createBuild } from '../create_build'; +import { createPackage } from '../create_package'; +import { pluginConfig } from '../../../lib'; const PLUGIN_FIXTURE = resolve(__dirname, '__fixtures__/create_package_test_plugin'); -const PLUGIN = require('../../../lib/plugin_config')(PLUGIN_FIXTURE); +const PLUGIN = pluginConfig(PLUGIN_FIXTURE); const PLUGIN_BUILD_DIR = resolve(PLUGIN_FIXTURE, 'build-custom'); const buildVersion = PLUGIN.version; diff --git a/packages/kbn-plugin-helpers/tasks/build/rewrite_package_json.js b/packages/kbn-plugin-helpers/src/tasks/build/rewrite_package_json.ts similarity index 81% rename from packages/kbn-plugin-helpers/tasks/build/rewrite_package_json.js rename to packages/kbn-plugin-helpers/src/tasks/build/rewrite_package_json.ts index 64656baee6fd2b..255b2e6ef99922 100644 --- a/packages/kbn-plugin-helpers/tasks/build/rewrite_package_json.js +++ b/packages/kbn-plugin-helpers/src/tasks/build/rewrite_package_json.ts @@ -17,13 +17,18 @@ * under the License. */ -const map = require('through2-map').obj; -const gitInfo = require('./git_info'); +import Through2Map from 'through2-map'; +import File from 'vinyl'; +import { gitInfo } from './git_info'; -module.exports = function rewritePackage(buildSource, buildVersion, kibanaVersion) { - return map(function(file) { +export function rewritePackageJson( + buildSource: string, + buildVersion: string, + kibanaVersion: string +) { + return Through2Map.obj(function(file: File) { if (file.basename === 'package.json' && file.dirname === buildSource) { - const pkg = JSON.parse(file.contents.toString('utf8')); + const pkg = JSON.parse(file.contents!.toString('utf8')); // rewrite the target kibana version while the // file is on it's way to the archive @@ -46,4 +51,4 @@ module.exports = function rewritePackage(buildSource, buildVersion, kibanaVersio return file; }); -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/start/README.md b/packages/kbn-plugin-helpers/src/tasks/start/README.md similarity index 100% rename from packages/kbn-plugin-helpers/tasks/start/README.md rename to packages/kbn-plugin-helpers/src/tasks/start/README.md diff --git a/packages/kbn-plugin-helpers/tasks/build/index.js b/packages/kbn-plugin-helpers/src/tasks/start/index.ts similarity index 94% rename from packages/kbn-plugin-helpers/tasks/build/index.js rename to packages/kbn-plugin-helpers/src/tasks/start/index.ts index b01a64c7f9be24..cf34bdbadf4166 100644 --- a/packages/kbn-plugin-helpers/tasks/build/index.js +++ b/packages/kbn-plugin-helpers/src/tasks/start/index.ts @@ -17,4 +17,4 @@ * under the License. */ -module.exports = require('./build_action'); +export * from './start_task'; diff --git a/packages/kbn-plugin-helpers/tasks/start/start_action.js b/packages/kbn-plugin-helpers/src/tasks/start/start_task.ts similarity index 85% rename from packages/kbn-plugin-helpers/tasks/start/start_action.js rename to packages/kbn-plugin-helpers/src/tasks/start/start_task.ts index 08ef0914311f83..75affb6da8c6f1 100644 --- a/packages/kbn-plugin-helpers/tasks/start/start_action.js +++ b/packages/kbn-plugin-helpers/src/tasks/start/start_task.ts @@ -17,11 +17,15 @@ * under the License. */ -const execFileSync = require('child_process').execFileSync; -const { join } = require('path'); -const split = require('argv-split'); +import { execFileSync } from 'child_process'; +import { join } from 'path'; -module.exports = function(plugin, run, options) { +// @ts-ignore +import split from 'argv-split'; + +import { TaskContext } from '../../lib'; + +export function startTask({ plugin, options }: TaskContext) { options = options || {}; const cmd = 'node'; @@ -44,4 +48,4 @@ module.exports = function(plugin, run, options) { cwd: plugin.kibanaRoot, stdio: ['ignore', 1, 2], }); -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/test/all/README.md b/packages/kbn-plugin-helpers/src/tasks/test/all/README.md similarity index 100% rename from packages/kbn-plugin-helpers/tasks/test/all/README.md rename to packages/kbn-plugin-helpers/src/tasks/test/all/README.md diff --git a/packages/kbn-plugin-helpers/tasks/test/all/index.js b/packages/kbn-plugin-helpers/src/tasks/test/all/index.ts similarity index 94% rename from packages/kbn-plugin-helpers/tasks/test/all/index.js rename to packages/kbn-plugin-helpers/src/tasks/test/all/index.ts index 0cdc615ee867fd..be8db50825fc96 100644 --- a/packages/kbn-plugin-helpers/tasks/test/all/index.js +++ b/packages/kbn-plugin-helpers/src/tasks/test/all/index.ts @@ -17,4 +17,4 @@ * under the License. */ -module.exports = require('./test_all_action'); +export * from './test_all_task'; diff --git a/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js b/packages/kbn-plugin-helpers/src/tasks/test/all/test_all_task.ts similarity index 89% rename from packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js rename to packages/kbn-plugin-helpers/src/tasks/test/all/test_all_task.ts index f16e391b2b2113..d07c19291d2cbd 100644 --- a/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js +++ b/packages/kbn-plugin-helpers/src/tasks/test/all/test_all_task.ts @@ -17,7 +17,9 @@ * under the License. */ -module.exports = function testAllAction(plugin, run) { +import { TaskContext } from '../../../lib'; + +export function testAllTask({ run }: TaskContext) { run('testMocha'); run('testKarma'); -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/test/karma/README.md b/packages/kbn-plugin-helpers/src/tasks/test/karma/README.md similarity index 100% rename from packages/kbn-plugin-helpers/tasks/test/karma/README.md rename to packages/kbn-plugin-helpers/src/tasks/test/karma/README.md diff --git a/packages/kbn-plugin-helpers/tasks/test/karma/index.js b/packages/kbn-plugin-helpers/src/tasks/test/karma/index.ts similarity index 94% rename from packages/kbn-plugin-helpers/tasks/test/karma/index.js rename to packages/kbn-plugin-helpers/src/tasks/test/karma/index.ts index 0472207948c3d7..3089357b499910 100644 --- a/packages/kbn-plugin-helpers/tasks/test/karma/index.js +++ b/packages/kbn-plugin-helpers/src/tasks/test/karma/index.ts @@ -17,4 +17,4 @@ * under the License. */ -module.exports = require('./test_karma_action'); +export * from './test_karma_task'; diff --git a/packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js b/packages/kbn-plugin-helpers/src/tasks/test/karma/test_karma_task.ts similarity index 86% rename from packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js rename to packages/kbn-plugin-helpers/src/tasks/test/karma/test_karma_task.ts index e1ba8caee7f9a1..2fe8134209894f 100644 --- a/packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js +++ b/packages/kbn-plugin-helpers/src/tasks/test/karma/test_karma_task.ts @@ -17,10 +17,12 @@ * under the License. */ -const execFileSync = require('child_process').execFileSync; -const winCmd = require('../../../lib/win_cmd'); +import { execFileSync } from 'child_process'; -module.exports = function testKarmaAction(plugin, run, options) { +import { TaskContext } from '../../../lib'; +import { winCmd } from '../../../lib/win_cmd'; + +export function testKarmaTask({ plugin, options }: TaskContext) { options = options || {}; const kbnServerArgs = ['--kbnServer.plugin-path=' + plugin.root]; @@ -37,4 +39,4 @@ module.exports = function testKarmaAction(plugin, run, options) { cwd: plugin.kibanaRoot, stdio: ['ignore', 1, 2], }); -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/test/mocha/README.md b/packages/kbn-plugin-helpers/src/tasks/test/mocha/README.md similarity index 100% rename from packages/kbn-plugin-helpers/tasks/test/mocha/README.md rename to packages/kbn-plugin-helpers/src/tasks/test/mocha/README.md diff --git a/packages/kbn-plugin-helpers/src/tasks/test/mocha/index.ts b/packages/kbn-plugin-helpers/src/tasks/test/mocha/index.ts new file mode 100644 index 00000000000000..74e53062d4f01a --- /dev/null +++ b/packages/kbn-plugin-helpers/src/tasks/test/mocha/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './test_mocha_task'; diff --git a/packages/kbn-plugin-helpers/tasks/test/mocha/test_mocha_action.js b/packages/kbn-plugin-helpers/src/tasks/test/mocha/test_mocha_task.ts similarity index 86% rename from packages/kbn-plugin-helpers/tasks/test/mocha/test_mocha_action.js rename to packages/kbn-plugin-helpers/src/tasks/test/mocha/test_mocha_task.ts index 7cc9864baa9756..a79ef0a59d19a9 100644 --- a/packages/kbn-plugin-helpers/tasks/test/mocha/test_mocha_action.js +++ b/packages/kbn-plugin-helpers/src/tasks/test/mocha/test_mocha_task.ts @@ -17,10 +17,12 @@ * under the License. */ -const execFileSync = require('child_process').execFileSync; -const globby = require('globby'); +import { execFileSync } from 'child_process'; +import globby from 'globby'; -module.exports = function(plugin, run, options) { +import { TaskContext } from '../../../lib'; + +export function testMochaTask({ plugin, options }: TaskContext) { options = options || {}; let testPatterns = plugin.serverTestPatterns; @@ -43,4 +45,4 @@ module.exports = function(plugin, run, options) { stdio: ['ignore', 1, 2], } ); -}; +} diff --git a/packages/kbn-plugin-helpers/tasks/build/create_build.js b/packages/kbn-plugin-helpers/tasks/build/create_build.js deleted file mode 100644 index 7d42dc13ef278d..00000000000000 --- a/packages/kbn-plugin-helpers/tasks/build/create_build.js +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const path = require('path'); -const relative = require('path').relative; -const { readFileSync, writeFileSync, unlinkSync, existsSync } = require('fs'); -const execa = require('execa'); -const sass = require('node-sass'); -const del = require('del'); -const vfs = require('vinyl-fs'); -const rename = require('gulp-rename'); -const through = require('through2'); -const minimatch = require('minimatch'); -const gulpBabel = require('gulp-babel'); -const { promisify } = require('util'); -const { pipeline } = require('stream'); - -const rewritePackageJson = require('./rewrite_package_json'); -const winCmd = require('../../lib/win_cmd'); - -const asyncPipeline = promisify(pipeline); - -// `link:` dependencies create symlinks, but we don't want to include symlinks -// in the built zip file. Therefore we remove all symlinked dependencies, so we -// can re-create them when installing the plugin. -function removeSymlinkDependencies(root) { - const nodeModulesPattern = path.join(root, '**', 'node_modules', '**'); - - return through.obj((file, enc, cb) => { - const isSymlink = file.symlink != null; - const isDependency = minimatch(file.path, nodeModulesPattern); - - if (isSymlink && isDependency) { - unlinkSync(file.path); - } - - cb(); - }); -} - -// parse a ts config file -function parseTsconfig(pluginSourcePath, configPath) { - const ts = require(path.join(pluginSourcePath, 'node_modules', 'typescript')); // eslint-disable-line import/no-dynamic-require - - const { error, config } = ts.parseConfigFileTextToJson( - configPath, - readFileSync(configPath, 'utf8') - ); - - if (error) { - throw error; - } - - return config; -} - -// transpile with babel -async function transpileWithBabel(srcGlobs, buildRoot, presets) { - await asyncPipeline( - vfs.src( - srcGlobs.concat([ - '!**/*.d.ts', - '!**/*.{test,test.mocks,mock,mocks}.{ts,tsx}', - '!**/node_modules/**', - '!**/bower_components/**', - '!**/__tests__/**', - ]), - { - cwd: buildRoot, - } - ), - - gulpBabel({ - babelrc: false, - presets, - }), - - vfs.dest(buildRoot) - ); -} - -module.exports = function createBuild(plugin, buildTarget, buildVersion, kibanaVersion, files) { - const buildSource = plugin.root; - const buildRoot = path.join(buildTarget, 'kibana', plugin.id); - - return del(buildTarget) - .then(function() { - return new Promise(function(resolve, reject) { - vfs - .src(files, { - cwd: buildSource, - base: buildSource, - allowEmpty: true, - }) - // modify the package.json file - .pipe(rewritePackageJson(buildSource, buildVersion, kibanaVersion)) - - // put all files inside the correct directories - .pipe( - rename(function nestFileInDir(filePath) { - const nonRelativeDirname = filePath.dirname.replace(/^(\.\.\/?)+/g, ''); - filePath.dirname = path.join(relative(buildTarget, buildRoot), nonRelativeDirname); - }) - ) - - .pipe(vfs.dest(buildTarget)) - .on('end', resolve) - .on('error', reject); - }); - }) - .then(function() { - if (plugin.skipInstallDependencies) { - return; - } - - // install packages in build - execa.sync(winCmd('yarn'), ['install', '--production', '--pure-lockfile'], { - cwd: buildRoot, - }); - }) - .then(function() { - if (!plugin.styleSheetToCompile) { - return; - } - - const file = path.resolve(plugin.root, plugin.styleSheetToCompile); - if (!existsSync(file)) { - throw new Error(`Path provided for styleSheetToCompile does not exist: ${file}`); - } - - const outputFileName = path.basename(file, path.extname(file)) + '.css'; - const output = path.join(buildRoot, path.dirname(plugin.styleSheetToCompile), outputFileName); - - const rendered = sass.renderSync({ file, output }); - writeFileSync(output, rendered.css); - - del.sync([path.join(buildRoot, '**', '*.s{a,c}ss')]); - }) - .then(async function() { - const buildConfigPath = path.join(buildRoot, 'tsconfig.json'); - - if (!existsSync(buildConfigPath)) { - return; - } - - // attempt to patch the extends path in the tsconfig file - const buildConfig = parseTsconfig(buildSource, buildConfigPath); - - if (buildConfig.extends) { - buildConfig.extends = path.join(relative(buildRoot, buildSource), buildConfig.extends); - - writeFileSync(buildConfigPath, JSON.stringify(buildConfig)); - } - - // Transpile ts server code - // - // Include everything except content from public folders - await transpileWithBabel(['**/*.{ts,tsx}', '!**/public/**'], buildRoot, [ - require.resolve('@kbn/babel-preset/node_preset'), - ]); - - // Transpile ts client code - // - // Include everything inside a public directory - await transpileWithBabel(['**/public/**/*.{ts,tsx}'], buildRoot, [ - require.resolve('@kbn/babel-preset/webpack_preset'), - ]); - - del.sync([ - path.join(buildRoot, '**', '*.{ts,tsx,d.ts}'), - path.join(buildRoot, 'tsconfig.json'), - ]); - }) - .then(function() { - const buildFiles = [relative(buildTarget, buildRoot) + '/**/*']; - - return new Promise((resolve, reject) => { - vfs - .src(buildFiles, { - cwd: buildTarget, - base: buildTarget, - resolveSymlinks: false, - }) - .pipe(removeSymlinkDependencies(buildRoot)) - .on('finish', resolve) - .on('error', reject); - }); - }); -}; diff --git a/packages/kbn-plugin-helpers/tasks/test/mocha/index.js b/packages/kbn-plugin-helpers/tasks/test/mocha/index.js deleted file mode 100644 index 1ab022db588f92..00000000000000 --- a/packages/kbn-plugin-helpers/tasks/test/mocha/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -module.exports = require('./test_mocha_action'); diff --git a/packages/kbn-plugin-helpers/tsconfig.json b/packages/kbn-plugin-helpers/tsconfig.json index f5559aa7290c5d..d0dbe1e44f0fa5 100644 --- a/packages/kbn-plugin-helpers/tsconfig.json +++ b/packages/kbn-plugin-helpers/tsconfig.json @@ -1,4 +1,11 @@ { "extends": "../../tsconfig.json", - "include": ["lib/index.d.ts"] + "compilerOptions": { + "outDir": "target", + "declaration": true, + "sourceMap": true + }, + "include": [ + "src/**/*.ts" + ] } diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index 57cdc8ffd494f3..585ce8181df5f4 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -42,10 +42,8 @@ export { kbnTestConfig, kibanaServerTestUser, kibanaTestUser, adminTestUser } fr // @ts-ignore not typed yet export { setupUsers, DEFAULT_SUPERUSER_PASS } from './functional_tests/lib/auth'; -// @ts-ignore not typed yet export { readConfigFile } from './functional_test_runner/lib/config/read_config_file'; -// @ts-ignore not typed yet export { runFtrCli } from './functional_test_runner/cli'; export { diff --git a/renovate.json5 b/renovate.json5 index c4efa86366bf40..f5bb39a16fe465 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -426,6 +426,14 @@ '@types/indent-string', ], }, + { + groupSlug: 'inquirer', + groupName: 'inquirer related packages', + packageNames: [ + 'inquirer', + '@types/inquirer', + ], + }, { groupSlug: 'intl-relativeformat', groupName: 'intl-relativeformat related packages', @@ -683,6 +691,14 @@ '@types/node-forge', ], }, + { + groupSlug: 'node-sass', + groupName: 'node-sass related packages', + packageNames: [ + 'node-sass', + '@types/node-sass', + ], + }, { groupSlug: 'nodemailer', groupName: 'nodemailer related packages', @@ -951,6 +967,22 @@ '@types/tempy', ], }, + { + groupSlug: 'through2', + groupName: 'through2 related packages', + packageNames: [ + 'through2', + '@types/through2', + ], + }, + { + groupSlug: 'through2-map', + groupName: 'through2-map related packages', + packageNames: [ + 'through2-map', + '@types/through2-map', + ], + }, { groupSlug: 'tinycolor2', groupName: 'tinycolor2 related packages', @@ -1003,6 +1035,14 @@ ], enabled: false, }, + { + groupSlug: 'vinyl', + groupName: 'vinyl related packages', + packageNames: [ + 'vinyl', + '@types/vinyl', + ], + }, { groupSlug: 'vinyl-fs', groupName: 'vinyl-fs related packages', diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index a42719417a2b17..2979dd7661e593 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -197,6 +197,11 @@ describe('#setup()', () => { expect(MockInjectedMetadataService.setup).toHaveBeenCalledTimes(1); }); + it('calls docLinks#setup()', async () => { + await setupCore(); + expect(MockDocLinksService.setup).toHaveBeenCalledTimes(1); + }); + it('calls http#setup()', async () => { await setupCore(); expect(MockHttpService.setup).toHaveBeenCalledTimes(1); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index e58114b69dcc1e..46e1ecb83e9e4e 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -160,6 +160,7 @@ export class CoreSystem { i18n: this.i18n.getContext(), }); await this.integrations.setup(); + const docLinks = this.docLinks.setup({ injectedMetadata }); const http = this.http.setup({ injectedMetadata, fatalErrors: this.fatalErrorsSetup }); const uiSettings = this.uiSettings.setup({ http, injectedMetadata }); const notifications = this.notifications.setup({ uiSettings }); @@ -180,6 +181,7 @@ export class CoreSystem { const core: InternalCoreSetup = { application, context, + docLinks, fatalErrors: this.fatalErrorsSetup, http, injectedMetadata, @@ -211,7 +213,7 @@ export class CoreSystem { try { const injectedMetadata = await this.injectedMetadata.start(); const uiSettings = await this.uiSettings.start(); - const docLinks = await this.docLinks.start({ injectedMetadata }); + const docLinks = this.docLinks.start(); const http = await this.http.start(); const savedObjects = await this.savedObjects.start({ http }); const i18n = await this.i18n.start(); diff --git a/src/core/public/doc_links/doc_links_service.mock.ts b/src/core/public/doc_links/doc_links_service.mock.ts index 239249f40fe66d..9edcf2e3c79901 100644 --- a/src/core/public/doc_links/doc_links_service.mock.ts +++ b/src/core/public/doc_links/doc_links_service.mock.ts @@ -18,21 +18,25 @@ */ import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; -import { DocLinksService, DocLinksStart } from './doc_links_service'; +import { DocLinksService, DocLinksSetup, DocLinksStart } from './doc_links_service'; -const createStartContractMock = (): DocLinksStart => { +const createSetupContractMock = (): DocLinksSetup => { // This service is so simple that we actually use the real implementation const injectedMetadata = injectedMetadataServiceMock.createStartContract(); injectedMetadata.getKibanaBranch.mockReturnValue('mocked-test-branch'); - return new DocLinksService().start({ injectedMetadata }); + return new DocLinksService().setup({ injectedMetadata }); }; +const createStartContractMock: () => DocLinksStart = createSetupContractMock; + type DocLinksServiceContract = PublicMethodsOf; const createMock = (): jest.Mocked => ({ + setup: jest.fn().mockReturnValue(createSetupContractMock()), start: jest.fn().mockReturnValue(createStartContractMock()), }); export const docLinksServiceMock = { create: createMock, + createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, }; diff --git a/src/core/public/doc_links/doc_links_service.test.ts b/src/core/public/doc_links/doc_links_service.test.ts index 283bfa9e2771a7..4c5d6bcde8b773 100644 --- a/src/core/public/doc_links/doc_links_service.test.ts +++ b/src/core/public/doc_links/doc_links_service.test.ts @@ -20,15 +20,33 @@ import { DocLinksService } from './doc_links_service'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; -describe('DocLinksService#start()', () => { +describe('DocLinksService#setup()', () => { it('templates the doc links with the branch information from injectedMetadata', () => { const injectedMetadata = injectedMetadataServiceMock.createStartContract(); injectedMetadata.getKibanaBranch.mockReturnValue('test-branch'); const service = new DocLinksService(); - const start = service.start({ injectedMetadata }); - expect(start.DOC_LINK_VERSION).toEqual('test-branch'); - expect(start.links.kibana).toEqual( + const setup = service.setup({ injectedMetadata }); + expect(setup.DOC_LINK_VERSION).toEqual('test-branch'); + expect(setup.links.kibana).toEqual( 'https://www.elastic.co/guide/en/kibana/test-branch/index.html' ); }); }); + +describe('DocLinksService#start()', () => { + it('returns the same data as setup', () => { + const injectedMetadata = injectedMetadataServiceMock.createStartContract(); + injectedMetadata.getKibanaBranch.mockReturnValue('test-branch'); + const service = new DocLinksService(); + const setup = service.setup({ injectedMetadata }); + const start = service.start(); + expect(setup).toEqual(start); + }); + + it('must be called after setup', () => { + const service = new DocLinksService(); + expect(() => { + service.start(); + }).toThrowErrorMatchingInlineSnapshot(`"DocLinksService#setup() must be called first!"`); + }); +}); diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 9b672d40961d8e..aeeb8c3342e462 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -17,21 +17,23 @@ * under the License. */ -import { InjectedMetadataStart } from '../injected_metadata'; +import { InjectedMetadataSetup } from '../injected_metadata'; import { deepFreeze } from '../../utils'; -interface StartDeps { - injectedMetadata: InjectedMetadataStart; +interface SetupDeps { + injectedMetadata: InjectedMetadataSetup; } /** @internal */ export class DocLinksService { - public start({ injectedMetadata }: StartDeps): DocLinksStart { + private service?: DocLinksSetup; + + public setup({ injectedMetadata }: SetupDeps): DocLinksSetup { const DOC_LINK_VERSION = injectedMetadata.getKibanaBranch(); const ELASTIC_WEBSITE_URL = 'https://www.elastic.co/'; const ELASTICSEARCH_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`; - return deepFreeze({ + this.service = deepFreeze({ DOC_LINK_VERSION, ELASTIC_WEBSITE_URL, links: { @@ -124,11 +126,21 @@ export class DocLinksService { }, }, }); + + return this.service; + } + + public start(): DocLinksStart { + if (!this.service) { + throw new Error(`DocLinksService#setup() must be called first!`); + } + + return this.service; } } /** @public */ -export interface DocLinksStart { +export interface DocLinksSetup { readonly DOC_LINK_VERSION: string; readonly ELASTIC_WEBSITE_URL: string; readonly links: { @@ -218,3 +230,6 @@ export interface DocLinksStart { readonly management: Record; }; } + +/** @public */ +export type DocLinksStart = DocLinksSetup; diff --git a/src/core/public/doc_links/index.ts b/src/core/public/doc_links/index.ts index fe49d4a7c6a583..fbfa9db5635ddc 100644 --- a/src/core/public/doc_links/index.ts +++ b/src/core/public/doc_links/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { DocLinksService, DocLinksStart } from './doc_links_service'; +export { DocLinksService, DocLinksSetup, DocLinksStart } from './doc_links_service'; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 3b2d9ed3c0b022..3698fdcfe95129 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -65,7 +65,7 @@ import { OverlayStart } from './overlays'; import { Plugin, PluginInitializer, PluginInitializerContext, PluginOpaqueId } from './plugins'; import { UiSettingsState, IUiSettingsClient } from './ui_settings'; import { ApplicationSetup, Capabilities, ApplicationStart } from './application'; -import { DocLinksStart } from './doc_links'; +import { DocLinksSetup, DocLinksStart } from './doc_links'; import { SavedObjectsStart } from './saved_objects'; export { PackageInfo, EnvironmentMode } from '../server/types'; import { @@ -208,6 +208,8 @@ export interface CoreSetup, any, any]>, []>(() => Promise.resolve([createCoreStartMock({ basePath }), pluginStartDeps, pluginStartContract]) diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index 19cfadf70be1b8..c4b3c929415ee7 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -101,6 +101,7 @@ export function createPluginSetupContext< deps.application.registerMountContext(plugin.opaqueId, contextName, provider), }, context: deps.context, + docLinks: deps.docLinks, fatalErrors: deps.fatalErrors, http: deps.http, notifications: deps.notifications, diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 6d71844bc19c84..6c5ab5fcedcfdd 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -89,6 +89,7 @@ describe('PluginsService', () => { mockSetupDeps = { application: applicationServiceMock.createInternalSetupContract(), context: contextServiceMock.createSetupContract(), + docLinks: docLinksServiceMock.createSetupContract(), fatalErrors: fatalErrorsServiceMock.createSetupContract(), http: httpServiceMock.createSetupContract(), injectedMetadata: pick(injectedMetadataServiceMock.createStartContract(), 'getInjectedVar'), diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 225ef611c0298f..fbd8f474151fa7 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -383,6 +383,8 @@ export interface CoreSetup; @@ -471,7 +473,7 @@ export const DEFAULT_APP_CATEGORIES: Readonly<{ }>; // @public (undocumented) -export interface DocLinksStart { +export interface DocLinksSetup { // (undocumented) readonly DOC_LINK_VERSION: string; // (undocumented) @@ -565,6 +567,9 @@ export interface DocLinksStart { }; } +// @public (undocumented) +export type DocLinksStart = DocLinksSetup; + // @public (undocumented) export interface EnvironmentMode { // (undocumented) diff --git a/src/core/server/http/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index 4ce422e1f65c44..0ca87eae6e2353 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -62,6 +62,7 @@ configService.atPath.mockReturnValue( disableProtection: true, whitelist: [], }, + customResponseHeaders: {}, } as any) ); diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index 7ac707b0f3d83e..0976bfd56682ec 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -18,7 +18,8 @@ */ import uuid from 'uuid'; -import { config } from '.'; +import { config, HttpConfig } from './http_config'; +import { CspConfig } from '../csp'; const validHostnames = ['www.example.com', '8.8.8.8', '::1', 'localhost']; const invalidHostname = 'asdf$%^'; @@ -107,6 +108,23 @@ test('throws if xsrf.whitelist element does not start with a slash', () => { ); }); +test('accepts any type of objects for custom headers', () => { + const httpSchema = config.schema; + const obj = { + customResponseHeaders: { + string: 'string', + bool: true, + number: 12, + array: [1, 2, 3], + nested: { + foo: 1, + bar: 'dolly', + }, + }, + }; + expect(() => httpSchema.validate(obj)).not.toThrow(); +}); + describe('with TLS', () => { test('throws if TLS is enabled but `redirectHttpFromPort` is equal to `port`', () => { const httpSchema = config.schema; @@ -173,3 +191,30 @@ describe('with compression', () => { expect(() => httpSchema.validate(obj)).toThrowErrorMatchingSnapshot(); }); }); + +describe('HttpConfig', () => { + it('converts customResponseHeaders to strings or arrays of strings', () => { + const httpSchema = config.schema; + const rawConfig = httpSchema.validate({ + customResponseHeaders: { + string: 'string', + bool: true, + number: 12, + array: [1, 2, 3], + nested: { + foo: 1, + bar: 'dolly', + }, + }, + }); + const httpConfig = new HttpConfig(rawConfig, CspConfig.DEFAULT); + + expect(httpConfig.customResponseHeaders).toEqual({ + string: 'string', + bool: 'true', + number: '12', + array: ['1', '2', '3'], + nested: '{"foo":1,"bar":"dolly"}', + }); + }); +}); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 73f44f3c5ab5ce..7c72e3270743e5 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -57,7 +57,7 @@ export const config = { ), schema.boolean({ defaultValue: false }) ), - customResponseHeaders: schema.recordOf(schema.string(), schema.string(), { + customResponseHeaders: schema.recordOf(schema.string(), schema.any(), { defaultValue: {}, }), host: schema.string({ @@ -136,7 +136,7 @@ export class HttpConfig { public socketTimeout: number; public port: number; public cors: boolean | { origin: string[] }; - public customResponseHeaders: Record; + public customResponseHeaders: Record; public maxPayload: ByteSizeValue; public basePath?: string; public rewriteBasePath: boolean; @@ -153,7 +153,15 @@ export class HttpConfig { this.host = rawHttpConfig.host; this.port = rawHttpConfig.port; this.cors = rawHttpConfig.cors; - this.customResponseHeaders = rawHttpConfig.customResponseHeaders; + this.customResponseHeaders = Object.entries(rawHttpConfig.customResponseHeaders ?? {}).reduce( + (headers, [key, value]) => { + return { + ...headers, + [key]: Array.isArray(value) ? value.map(e => convertHeader(e)) : convertHeader(value), + }; + }, + {} + ); this.maxPayload = rawHttpConfig.maxPayload; this.name = rawHttpConfig.name; this.basePath = rawHttpConfig.basePath; @@ -166,3 +174,7 @@ export class HttpConfig { this.xsrf = rawHttpConfig.xsrf; } } + +const convertHeader = (entry: any): string => { + return typeof entry === 'object' ? JSON.stringify(entry) : String(entry); +}; diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index 49c4d690c6876f..0e639aa72a8254 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -45,6 +45,7 @@ configService.atPath.mockReturnValue( disableProtection: true, whitelist: [], }, + customResponseHeaders: {}, } as any) ); diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 4e1f5981d6a410..6f5ecb1eb464b0 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -107,4 +107,5 @@ export const savedObjectsServiceMock = { createInternalStartContract: createInternalStartContractMock, createStartContract: createStartContractMock, createMigrationContext: migrationMocks.createContext, + createTypeRegistryMock: typeRegistryMock.create, }; diff --git a/src/legacy/core_plugins/kibana/public/index.ts b/src/legacy/core_plugins/kibana/public/index.ts index be22652ab2c1c7..6b1b7f0d249ff4 100644 --- a/src/legacy/core_plugins/kibana/public/index.ts +++ b/src/legacy/core_plugins/kibana/public/index.ts @@ -20,4 +20,4 @@ export { ProcessedImportResponse, processImportResponse, -} from '../../../../plugins/saved_objects_management/public/lib'; +} from '../../../../plugins/saved_objects_management/public/lib'; // eslint-disable-line @kbn/eslint/no-restricted-paths diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/tabs.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/tabs.tsx index 247af7e20d5815..c727dcd3e3c650 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/tabs.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/tabs.tsx @@ -33,6 +33,7 @@ import { i18n } from '@kbn/i18n'; import { fieldWildcardMatcher } from '../../../../../../../../../plugins/kibana_utils/public'; import { IndexPatternManagementStart } from '../../../../../../../../../plugins/index_pattern_management/public'; import { IndexPattern, IndexPatternField } from '../../../../../../../../../plugins/data/public'; +import { META_FIELDS_SETTING } from '../../../../../../../../../plugins/data/common'; import { createEditIndexPatternPageStateContainer } from '../edit_index_pattern_state_container'; import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from '../constants'; import { SourceFiltersTable } from '../source_filters_table'; @@ -98,7 +99,7 @@ export function Tabs({ config, indexPattern, fields, services, history, location }, [indexPattern, indexPattern.fields, refreshFilters]); const fieldWildcardMatcherDecorated = useCallback( - (filters: string[]) => fieldWildcardMatcher(filters, config.get('metaFields')), + (filters: string[]) => fieldWildcardMatcher(filters, config.get(META_FIELDS_SETTING)), [config] ); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js index a8376c0e84bf9b..24bc4ba8fba5b0 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js @@ -18,6 +18,7 @@ */ import { management } from 'ui/management'; +import { ManagementSectionId } from '../../../../../../../plugins/management/public'; import './create_index_pattern_wizard'; import './edit_index_pattern'; import uiRoutes from 'ui/routes'; @@ -163,7 +164,7 @@ uiModules }; }); -management.getSection('kibana').register('index_patterns', { +management.getSection(ManagementSectionId.Kibana).register('index_patterns', { display: i18n.translate('kbn.management.indexPattern.sectionsHeader', { defaultMessage: 'Index Patterns', }), diff --git a/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js index 1a040e236351a5..275f1cb687e226 100644 --- a/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/server/ui_setting_defaults.js @@ -271,106 +271,6 @@ export function getUiSettingDefaults() { defaultMessage: 'The index to access if no index is set', }), }, - defaultColumns: { - name: i18n.translate('kbn.advancedSettings.defaultColumnsTitle', { - defaultMessage: 'Default columns', - }), - value: ['_source'], - description: i18n.translate('kbn.advancedSettings.defaultColumnsText', { - defaultMessage: 'Columns displayed by default in the Discovery tab', - }), - category: ['discover'], - }, - metaFields: { - name: i18n.translate('kbn.advancedSettings.metaFieldsTitle', { - defaultMessage: 'Meta fields', - }), - value: ['_source', '_id', '_type', '_index', '_score'], - description: i18n.translate('kbn.advancedSettings.metaFieldsText', { - defaultMessage: - 'Fields that exist outside of _source to merge into our document when displaying it', - }), - }, - 'discover:sampleSize': { - name: i18n.translate('kbn.advancedSettings.discover.sampleSizeTitle', { - defaultMessage: 'Number of rows', - }), - value: 500, - description: i18n.translate('kbn.advancedSettings.discover.sampleSizeText', { - defaultMessage: 'The number of rows to show in the table', - }), - category: ['discover'], - }, - 'discover:aggs:terms:size': { - name: i18n.translate('kbn.advancedSettings.discover.aggsTermsSizeTitle', { - defaultMessage: 'Number of terms', - }), - value: 20, - type: 'number', - description: i18n.translate('kbn.advancedSettings.discover.aggsTermsSizeText', { - defaultMessage: - 'Determines how many terms will be visualized when clicking the "visualize" ' + - 'button, in the field drop downs, in the discover sidebar.', - }), - category: ['discover'], - }, - 'discover:sort:defaultOrder': { - name: i18n.translate('kbn.advancedSettings.discover.sortDefaultOrderTitle', { - defaultMessage: 'Default sort direction', - }), - value: 'desc', - options: ['desc', 'asc'], - optionLabels: { - desc: i18n.translate('kbn.advancedSettings.discover.sortOrderDesc', { - defaultMessage: 'Descending', - }), - asc: i18n.translate('kbn.advancedSettings.discover.sortOrderAsc', { - defaultMessage: 'Ascending', - }), - }, - type: 'select', - description: i18n.translate('kbn.advancedSettings.discover.sortDefaultOrderText', { - defaultMessage: - 'Controls the default sort direction for time based index patterns in the Discover app.', - }), - category: ['discover'], - }, - 'discover:searchOnPageLoad': { - name: i18n.translate('kbn.advancedSettings.discover.searchOnPageLoadTitle', { - defaultMessage: 'Search on page load', - }), - value: true, - type: 'boolean', - description: i18n.translate('kbn.advancedSettings.discover.searchOnPageLoadText', { - defaultMessage: - 'Controls whether a search is executed when Discover first loads. This setting does not ' + - 'have an effect when loading a saved search.', - }), - category: ['discover'], - }, - 'doc_table:highlight': { - name: i18n.translate('kbn.advancedSettings.docTableHighlightTitle', { - defaultMessage: 'Highlight results', - }), - value: true, - description: i18n.translate('kbn.advancedSettings.docTableHighlightText', { - defaultMessage: - 'Highlight results in Discover and Saved Searches Dashboard. ' + - 'Highlighting makes requests slow when working on big documents.', - }), - category: ['discover'], - }, - 'doc_table:hideTimeColumn': { - name: i18n.translate('kbn.advancedSettings.docTableHideTimeColumnTitle', { - defaultMessage: "Hide 'Time' column", - }), - value: false, - description: i18n.translate('kbn.advancedSettings.docTableHideTimeColumnText', { - defaultMessage: - "Hide the 'Time' column in Discover and in all Saved Searches on Dashboards.", - }), - category: ['discover'], - }, 'courier:ignoreFilterIfFieldNotInIndex': { name: i18n.translate('kbn.advancedSettings.courier.ignoreFilterTitle', { defaultMessage: 'Ignore filter(s)', @@ -484,15 +384,6 @@ export function getUiSettingDefaults() { value: false, category: ['search'], }, - 'fields:popularLimit': { - name: i18n.translate('kbn.advancedSettings.fieldsPopularLimitTitle', { - defaultMessage: 'Popular fields limit', - }), - value: 10, - description: i18n.translate('kbn.advancedSettings.fieldsPopularLimitText', { - defaultMessage: 'The top N most popular fields to show', - }), - }, 'histogram:barTarget': { name: i18n.translate('kbn.advancedSettings.histogram.barTargetTitle', { defaultMessage: 'Target bars', @@ -602,56 +493,6 @@ export function getUiSettingDefaults() { }), category: ['visualization'], }, - 'visualization:colorMapping': { - name: i18n.translate('kbn.advancedSettings.visualization.colorMappingTitle', { - defaultMessage: 'Color mapping', - }), - value: JSON.stringify({ - Count: '#00A69B', - }), - type: 'json', - description: i18n.translate('kbn.advancedSettings.visualization.colorMappingText', { - defaultMessage: 'Maps values to specified colors within visualizations', - }), - category: ['visualization'], - }, - 'visualization:loadingDelay': { - name: i18n.translate('kbn.advancedSettings.visualization.loadingDelayTitle', { - defaultMessage: 'Loading delay', - }), - value: '2s', - description: i18n.translate('kbn.advancedSettings.visualization.loadingDelayText', { - defaultMessage: 'Time to wait before dimming visualizations during query', - }), - category: ['visualization'], - }, - 'visualization:dimmingOpacity': { - name: i18n.translate('kbn.advancedSettings.visualization.dimmingOpacityTitle', { - defaultMessage: 'Dimming opacity', - }), - value: 0.5, - type: 'number', - description: i18n.translate('kbn.advancedSettings.visualization.dimmingOpacityText', { - defaultMessage: - 'The opacity of the chart items that are dimmed when highlighting another element of the chart. ' + - 'The lower this number, the more the highlighted element will stand out. ' + - 'This must be a number between 0 and 1.', - }), - category: ['visualization'], - }, - 'visualization:heatmap:maxBuckets': { - name: i18n.translate('kbn.advancedSettings.visualization.heatmap.maxBucketsTitle', { - defaultMessage: 'Heatmap maximum buckets', - }), - value: 50, - type: 'number', - description: i18n.translate('kbn.advancedSettings.visualization.heatmap.maxBucketsText', { - defaultMessage: - 'The maximum number of buckets a single datasource can return. ' + - 'A higher number might have negative impact on browser rendering performance', - }), - category: ['visualization'], - }, 'csv:separator': { name: i18n.translate('kbn.advancedSettings.csv.separatorTitle', { defaultMessage: 'CSV separator', @@ -831,26 +672,6 @@ export function getUiSettingDefaults() { }, }), }, - 'savedObjects:perPage': { - name: i18n.translate('kbn.advancedSettings.savedObjects.perPageTitle', { - defaultMessage: 'Objects per page', - }), - value: 20, - type: 'number', - description: i18n.translate('kbn.advancedSettings.savedObjects.perPageText', { - defaultMessage: 'Number of objects to show per page in the load dialog', - }), - }, - 'savedObjects:listingLimit': { - name: i18n.translate('kbn.advancedSettings.savedObjects.listingLimitTitle', { - defaultMessage: 'Objects listing limit', - }), - type: 'number', - value: 1000, - description: i18n.translate('kbn.advancedSettings.savedObjects.listingLimitText', { - defaultMessage: 'Number of objects to fetch for the listing pages', - }), - }, 'timepicker:timeDefaults': { name: i18n.translate('kbn.advancedSettings.timepicker.timeDefaultsTitle', { defaultMessage: 'Time filter defaults', @@ -1097,15 +918,6 @@ export function getUiSettingDefaults() { type: 'number', category: ['notifications'], }, - 'metrics:max_buckets': { - name: i18n.translate('kbn.advancedSettings.maxBucketsTitle', { - defaultMessage: 'Maximum buckets', - }), - value: 2000, - description: i18n.translate('kbn.advancedSettings.maxBucketsText', { - defaultMessage: 'The maximum number of buckets a single datasource can return', - }), - }, 'state:storeInSessionStorage': { name: i18n.translate('kbn.advancedSettings.storeUrlTitle', { defaultMessage: 'Store URLs in session storage', @@ -1128,38 +940,6 @@ export function getUiSettingDefaults() { 'The placeholder for the "Index pattern name" field in "Management > Index Patterns > Create Index Pattern".', }), }, - 'context:defaultSize': { - name: i18n.translate('kbn.advancedSettings.context.defaultSizeTitle', { - defaultMessage: 'Context size', - }), - value: 5, - description: i18n.translate('kbn.advancedSettings.context.defaultSizeText', { - defaultMessage: 'The number of surrounding entries to show in the context view', - }), - category: ['discover'], - }, - 'context:step': { - name: i18n.translate('kbn.advancedSettings.context.sizeStepTitle', { - defaultMessage: 'Context size step', - }), - value: 5, - description: i18n.translate('kbn.advancedSettings.context.sizeStepText', { - defaultMessage: 'The step size to increment or decrement the context size by', - }), - category: ['discover'], - }, - 'context:tieBreakerFields': { - name: i18n.translate('kbn.advancedSettings.context.tieBreakerFieldsTitle', { - defaultMessage: 'Tie breaker fields', - }), - value: ['_doc'], - description: i18n.translate('kbn.advancedSettings.context.tieBreakerFieldsText', { - defaultMessage: - 'A comma-separated list of fields to use for tie-breaking between documents that have the same timestamp value. ' + - 'From this list the first field that is present and sortable in the current index pattern is used.', - }), - category: ['discover'], - }, 'accessibility:disableAnimations': { name: i18n.translate('kbn.advancedSettings.disableAnimationsTitle', { defaultMessage: 'Disable Animations', diff --git a/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js b/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js index fb54c36df27d71..ee729d2b427ad3 100644 --- a/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js +++ b/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js @@ -28,6 +28,7 @@ import { PaginateControlsDirectiveProvider, PaginateDirectiveProvider, } from '../../../../../plugins/kibana_legacy/public'; +import { PER_PAGE_SETTING } from '../../../../../plugins/saved_objects/common'; const module = uiModules.get('kibana'); @@ -65,7 +66,7 @@ module const $input = $element.find('input[ng-model=filter]'); // The number of items to show in the list - $scope.perPage = config.get('savedObjects:perPage'); + $scope.perPage = config.get(PER_PAGE_SETTING); // the list that will hold the suggestions const $list = $element.find('ul'); diff --git a/src/legacy/ui/public/private/index.d.ts b/src/legacy/ui/public/private/index.d.ts index 895dc639393118..3b692ba58cbe1f 100644 --- a/src/legacy/ui/public/private/index.d.ts +++ b/src/legacy/ui/public/private/index.d.ts @@ -17,4 +17,4 @@ * under the License. */ -export { IPrivate } from '../../../../plugins/kibana_legacy/public/utils/private'; +export { IPrivate } from '../../../../plugins/kibana_legacy/public/'; diff --git a/src/legacy/ui/public/url/index.js b/src/legacy/ui/public/url/index.js index f3beb05e577c1d..8ef267de2890c2 100644 --- a/src/legacy/ui/public/url/index.js +++ b/src/legacy/ui/public/url/index.js @@ -19,4 +19,5 @@ export { KbnUrlProvider } from './url'; export { RedirectWhenMissingProvider } from './redirect_when_missing'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { modifyUrl } from '../../../../core/utils'; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts index fe7f239fbea3bc..f9a2234d6e5a46 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/index.ts @@ -17,4 +17,5 @@ * under the License. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { buildPipeline } from '../../../../../../plugins/visualizations/public/legacy/build_pipeline'; diff --git a/src/plugins/advanced_settings/public/plugin.ts b/src/plugins/advanced_settings/public/plugin.ts index 04eeff1e1f3ce3..2784b74ab726cf 100644 --- a/src/plugins/advanced_settings/public/plugin.ts +++ b/src/plugins/advanced_settings/public/plugin.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { ManagementApp } from '../../management/public'; +import { ManagementApp, ManagementSectionId } from '../../management/public'; import { ComponentRegistry } from './component_registry'; import { AdvancedSettingsSetup, AdvancedSettingsStart, AdvancedSettingsPluginSetup } from './types'; @@ -32,15 +32,12 @@ export class AdvancedSettingsPlugin implements Plugin { private managementApp?: ManagementApp; public setup(core: CoreSetup, { management }: AdvancedSettingsPluginSetup) { - const kibanaSection = management.sections.getSection('kibana'); - if (!kibanaSection) { - throw new Error('`kibana` management section not found.'); - } + const kibanaSection = management.sections.getSection(ManagementSectionId.Kibana); this.managementApp = kibanaSection.registerApp({ id: 'settings', title, - order: 20, + order: 3, async mount(params) { const { mountManagementSection } = await import( './management_app/mount_management_section' diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts new file mode 100644 index 00000000000000..1ebf3bcb8f4b61 --- /dev/null +++ b/src/plugins/charts/common/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; diff --git a/src/plugins/charts/kibana.json b/src/plugins/charts/kibana.json index 43c41fe64306a6..9f4433e7099d8c 100644 --- a/src/plugins/charts/kibana.json +++ b/src/plugins/charts/kibana.json @@ -1,6 +1,6 @@ { "id": "charts", "version": "kibana", - "server": false, + "server": true, "ui": true } diff --git a/src/plugins/charts/public/services/colors/colors.test.ts b/src/plugins/charts/public/services/colors/colors.test.ts index 3e9012cd71dc5f..e3f99f2407f752 100644 --- a/src/plugins/charts/public/services/colors/colors.test.ts +++ b/src/plugins/charts/public/services/colors/colors.test.ts @@ -18,6 +18,7 @@ */ import { coreMock } from '../../../../../core/public/mocks'; +import { COLOR_MAPPING_SETTING } from '../../../common'; import { seedColors } from './seed_colors'; import { ColorsService } from './colors'; @@ -44,13 +45,13 @@ describe('Vislib Color Service', () => { const nullValue = null; beforeEach(() => { - previousConfig = config.get('visualization:colorMapping'); - config.set('visualization:colorMapping', {}); + previousConfig = config.get(COLOR_MAPPING_SETTING); + config.set(COLOR_MAPPING_SETTING, {}); color = colors.createColorLookupFunction(arr, {}); }); afterEach(() => { - config.set('visualization:colorMapping', previousConfig); + config.set(COLOR_MAPPING_SETTING, previousConfig); }); it('should throw error if not initialized', () => { diff --git a/src/plugins/charts/public/services/colors/mapped_colors.test.ts b/src/plugins/charts/public/services/colors/mapped_colors.test.ts index 77f1faf468b461..c3b9b0909051c1 100644 --- a/src/plugins/charts/public/services/colors/mapped_colors.test.ts +++ b/src/plugins/charts/public/services/colors/mapped_colors.test.ts @@ -21,6 +21,7 @@ import _ from 'lodash'; import d3 from 'd3'; import { coreMock } from '../../../../../core/public/mocks'; +import { COLOR_MAPPING_SETTING } from '../../../common'; import { seedColors } from './seed_colors'; import { MappedColors } from './mapped_colors'; @@ -36,16 +37,16 @@ describe('Mapped Colors', () => { let previousConfig: any; beforeEach(() => { - previousConfig = config.get('visualization:colorMapping'); + previousConfig = config.get(COLOR_MAPPING_SETTING); mappedColors.purge(); }); afterEach(() => { - config.set('visualization:colorMapping', previousConfig); + config.set(COLOR_MAPPING_SETTING, previousConfig); }); it('should properly map keys to unique colors', () => { - config.set('visualization:colorMapping', {}); + config.set(COLOR_MAPPING_SETTING, {}); const arr = [1, 2, 3, 4, 5]; mappedColors.mapKeys(arr); @@ -59,7 +60,7 @@ describe('Mapped Colors', () => { it('should not include colors used by the config', () => { const newConfig = { bar: seedColors[0] }; - config.set('visualization:colorMapping', newConfig); + config.set(COLOR_MAPPING_SETTING, newConfig); const arr = ['foo', 'baz', 'qux']; mappedColors.mapKeys(arr); @@ -71,7 +72,7 @@ describe('Mapped Colors', () => { it('should create a unique array of colors even when config is set', () => { const newConfig = { bar: seedColors[0] }; - config.set('visualization:colorMapping', newConfig); + config.set(COLOR_MAPPING_SETTING, newConfig); const arr = ['foo', 'bar', 'baz', 'qux']; mappedColors.mapKeys(arr); @@ -92,7 +93,7 @@ describe('Mapped Colors', () => { const color = d3.rgb(seedColors[0]); const rgb = `rgb(${color.r}, ${color.g}, ${color.b})`; const newConfig = { bar: rgb }; - config.set('visualization:colorMapping', newConfig); + config.set(COLOR_MAPPING_SETTING, newConfig); const arr = ['foo', 'bar', 'baz', 'qux']; mappedColors.mapKeys(arr); diff --git a/src/plugins/charts/public/services/colors/mapped_colors.ts b/src/plugins/charts/public/services/colors/mapped_colors.ts index 1c6ed690df6325..1469d357e7e79f 100644 --- a/src/plugins/charts/public/services/colors/mapped_colors.ts +++ b/src/plugins/charts/public/services/colors/mapped_colors.ts @@ -22,6 +22,7 @@ import d3 from 'd3'; import { CoreSetup } from 'kibana/public'; +import { COLOR_MAPPING_SETTING } from '../../../common'; import { createColorPalette } from './color_palette'; const standardizeColor = (color: string) => d3.rgb(color).toString(); @@ -41,7 +42,7 @@ export class MappedColors { } private getConfigColorMapping() { - return _.mapValues(this.uiSettings.get('visualization:colorMapping'), standardizeColor); + return _.mapValues(this.uiSettings.get(COLOR_MAPPING_SETTING), standardizeColor); } public get oldMap(): any { diff --git a/src/plugins/charts/server/index.ts b/src/plugins/charts/server/index.ts new file mode 100644 index 00000000000000..75a57ab6b405c2 --- /dev/null +++ b/src/plugins/charts/server/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ChartsServerPlugin } from './plugin'; + +export const plugin = () => new ChartsServerPlugin(); diff --git a/src/plugins/charts/server/plugin.ts b/src/plugins/charts/server/plugin.ts new file mode 100644 index 00000000000000..6bf45fb804469a --- /dev/null +++ b/src/plugins/charts/server/plugin.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import { CoreSetup, Plugin } from 'kibana/server'; +import { COLOR_MAPPING_SETTING } from '../common'; + +export class ChartsServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.uiSettings.register({ + [COLOR_MAPPING_SETTING]: { + name: i18n.translate('charts.advancedSettings.visualization.colorMappingTitle', { + defaultMessage: 'Color mapping', + }), + value: JSON.stringify({ + Count: '#00A69B', + }), + type: 'json', + description: i18n.translate('charts.advancedSettings.visualization.colorMappingText', { + defaultMessage: 'Maps values to specified colors within visualizations', + }), + category: ['visualization'], + schema: schema.string(), + }, + }); + + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts index 46df7036c09cd9..8e29d2734065e8 100644 --- a/src/plugins/dashboard/public/application/application.ts +++ b/src/plugins/dashboard/public/application/application.ts @@ -25,7 +25,6 @@ import angular, { IModule } from 'angular'; import 'angular-sanitize'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { - AppMountContext, ChromeStart, IUiSettingsClient, CoreStart, @@ -42,7 +41,7 @@ import { NavigationPublicPluginStart as NavigationStart } from '../../../navigat import { DataPublicPluginStart } from '../../../data/public'; import { SharePluginStart } from '../../../share/public'; import { KibanaLegacyStart, configureAppAngularModule } from '../../../kibana_legacy/public'; -import { SavedObjectLoader } from '../../../saved_objects/public'; +import { SavedObjectLoader, SavedObjectsStart } from '../../../saved_objects/public'; // required for i18nIdDirective import 'angular-sanitize'; @@ -73,13 +72,14 @@ export interface RenderDeps { usageCollection?: UsageCollectionSetup; navigateToDefaultApp: KibanaLegacyStart['navigateToDefaultApp']; scopedHistory: () => ScopedHistory; + savedObjects: SavedObjectsStart; } let angularModuleInstance: IModule | null = null; export const renderApp = (element: HTMLElement, appBasePath: string, deps: RenderDeps) => { if (!angularModuleInstance) { - angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation); + angularModuleInstance = createLocalAngularModule(); // global routing stuff configureAppAngularModule( angularModuleInstance, @@ -119,7 +119,7 @@ function mountDashboardApp(appBasePath: string, element: HTMLElement) { return $injector; } -function createLocalAngularModule(core: AppMountContext['core'], navigation: NavigationStart) { +function createLocalAngularModule() { createLocalI18nModule(); createLocalIconModule(); diff --git a/src/plugins/dashboard/public/application/legacy_app.js b/src/plugins/dashboard/public/application/legacy_app.js index d12d9de8c7dd49..4e3cc15d93ece0 100644 --- a/src/plugins/dashboard/public/application/legacy_app.js +++ b/src/plugins/dashboard/public/application/legacy_app.js @@ -29,7 +29,6 @@ import { createDashboardEditUrl, DashboardConstants } from '../dashboard_constan import { createKbnUrlStateStorage, redirectWhenMissing, - InvalidJSONProperty, SavedObjectNotFound, } from '../../../kibana_utils/public'; import { DashboardListing, EMPTY_FILTER } from './listing/dashboard_listing'; @@ -50,6 +49,7 @@ export function initDashboardApp(app, deps) { ['listingLimit', { watchDepth: 'reference' }], ['hideWriteControls', { watchDepth: 'reference' }], ['initialFilter', { watchDepth: 'reference' }], + ['initialPageSize', { watchDepth: 'reference' }], ]); }); @@ -109,7 +109,8 @@ export function initDashboardApp(app, deps) { kbnUrlStateStorage ); - $scope.listingLimit = deps.uiSettings.get('savedObjects:listingLimit'); + $scope.listingLimit = deps.savedObjects.settings.getListingLimit(); + $scope.initialPageSize = deps.savedObjects.settings.getPerPage(); $scope.create = () => { history.push(DashboardConstants.CREATE_NEW_DASHBOARD_URL); }; @@ -213,13 +214,6 @@ export function initDashboardApp(app, deps) { return savedDashboard; }) .catch(error => { - // A corrupt dashboard was detected (e.g. with invalid JSON properties) - if (error instanceof InvalidJSONProperty) { - deps.core.notifications.toasts.addDanger(error.message); - history.push(DashboardConstants.LANDING_PAGE_PATH); - return; - } - // Preserve BWC of v5.3.0 links for new, unsaved dashboards. // See https://github.com/elastic/kibana/issues/10951 for more context. if (error instanceof SavedObjectNotFound && id === 'create') { @@ -237,19 +231,12 @@ export function initDashboardApp(app, deps) { ); return new Promise(() => {}); } else { - throw error; + // E.g. a corrupt or deleted dashboard + deps.core.notifications.toasts.addDanger(error.message); + history.push(DashboardConstants.LANDING_PAGE_PATH); + return new Promise(() => {}); } - }) - .catch( - redirectWhenMissing({ - history, - navigateToApp: deps.core.application.navigateToApp, - mapping: { - dashboard: DashboardConstants.LANDING_PAGE_PATH, - }, - toastNotifications: deps.core.notifications.toasts, - }) - ); + }); }, }, }) diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.js b/src/plugins/dashboard/public/application/listing/dashboard_listing.js index c24e7e4617806b..c8cb551fbe561b 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.js +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.js @@ -50,6 +50,7 @@ export class DashboardListing extends React.Component { tableColumns={this.getTableColumns()} listingLimit={this.props.listingLimit} initialFilter={this.props.initialFilter} + initialPageSize={this.props.initialPageSize} noItemsFragment={this.getNoItemsMessage()} entityName={i18n.translate('dashboard.listing.table.entityName', { defaultMessage: 'dashboard', @@ -187,6 +188,7 @@ DashboardListing.propTypes = { listingLimit: PropTypes.number.isRequired, hideWriteControls: PropTypes.bool.isRequired, initialFilter: PropTypes.string, + initialPageSize: PropTypes.number.isRequired, }; DashboardListing.defaultProps = { diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html b/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html index f57c10d1a48dd2..ba05c138a0cba4 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing_ng_wrapper.html @@ -8,4 +8,5 @@ listing-limit="listingLimit" hide-write-controls="hideWriteControls" initial-filter="initialFilter" + initial-page-size="initialPageSize" > diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index e093342f957358..a5211fb3b5d7b3 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -33,6 +33,7 @@ export { DashboardConstants, createDashboardEditUrl } from './dashboard_constant export { DashboardStart, DashboardUrlGenerator } from './plugin'; export { DASHBOARD_APP_URL_GENERATOR } from './url_generator'; +export { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; export function plugin(initializerContext: PluginInitializerContext) { return new DashboardPlugin(initializerContext); diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 07c9feefa0c029..b4419adfe58da0 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,37 +33,29 @@ import { AppUpdater, ScopedHistory, } from 'src/core/public'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { UsageCollectionSetup } from '../../usage_collection/public'; +import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; +import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; +import { SharePluginSetup, SharePluginStart, UrlGeneratorContract } from '../../share/public'; +import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; + +import { Start as InspectorStartContract } from '../../inspector/public'; +import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; import { - CONTEXT_MENU_TRIGGER, - EmbeddableSetup, - EmbeddableStart, -} from '../../../plugins/embeddable/public'; -import { - DataPublicPluginStart, - DataPublicPluginSetup, - esFilters, -} from '../../../plugins/data/public'; -import { - SharePluginSetup, - SharePluginStart, - UrlGeneratorContract, -} from '../../../plugins/share/public'; -import { UiActionsSetup, UiActionsStart } from '../../../plugins/ui_actions/public'; - -import { Start as InspectorStartContract } from '../../../plugins/inspector/public'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../plugins/navigation/public'; -import { getSavedObjectFinder, SavedObjectLoader } from '../../../plugins/saved_objects/public'; + getSavedObjectFinder, + SavedObjectLoader, + SavedObjectsStart, +} from '../../saved_objects/public'; import { ExitFullScreenButton as ExitFullScreenButtonUi, ExitFullScreenButtonProps, -} from '../../../plugins/kibana_react/public'; -import { createKbnUrlTracker, Storage } from '../../../plugins/kibana_utils/public'; +} from '../../kibana_react/public'; +import { createKbnUrlTracker, Storage } from '../../kibana_utils/public'; import { KibanaLegacySetup, KibanaLegacyStart, initAngularBootstrap, -} from '../../../plugins/kibana_legacy/public'; +} from '../../kibana_legacy/public'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../../plugins/home/public'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; @@ -117,6 +109,7 @@ interface StartDependencies { savedObjectsClient: SavedObjectsClientContract; share?: SharePluginStart; uiActions: UiActionsStart; + savedObjects: SavedObjectsStart; } export type Setup = void; @@ -250,6 +243,7 @@ export class DashboardPlugin share: shareStart, data: dataStart, kibanaLegacy: { dashboardConfig, navigateToDefaultApp }, + savedObjects, } = pluginsStart; const deps: RenderDeps = { @@ -276,6 +270,7 @@ export class DashboardPlugin localStorage: new Storage(localStorage), usageCollection, scopedHistory: () => this.currentHistory!, + savedObjects, }; // make sure the index pattern list is up to date await dataStart.indexPatterns.clearCache(); diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index 00786a0c72cf1e..66a96e3e6e1293 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -18,3 +18,5 @@ */ export const DEFAULT_QUERY_LANGUAGE = 'kuery'; +export const META_FIELDS_SETTING = 'metaFields'; +export const DOC_HIGHLIGHT_SETTING = 'doc_table:highlight'; diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts index 4882e8eafc0d36..18853f7e292f68 100644 --- a/src/plugins/data/public/actions/select_range_action.ts +++ b/src/plugins/data/public/actions/select_range_action.ts @@ -53,16 +53,16 @@ export function selectRangeAction( }); }, isCompatible, - execute: async ({ timeFieldName, data }: SelectRangeActionContext) => { - if (!(await isCompatible({ timeFieldName, data }))) { + execute: async ({ data }: SelectRangeActionContext) => { + if (!(await isCompatible({ data }))) { throw new IncompatibleActionError(); } const selectedFilters = await createFiltersFromRangeSelectAction(data); - if (timeFieldName) { + if (data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - timeFieldName, + data.timeFieldName, selectedFilters ); filterManager.addFilters(restOfFilters); diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts index 210a58b3f75aa7..17c1b1b1e17695 100644 --- a/src/plugins/data/public/actions/value_click_action.ts +++ b/src/plugins/data/public/actions/value_click_action.ts @@ -57,12 +57,12 @@ export function valueClickAction( }); }, isCompatible, - execute: async (context: ValueClickActionContext) => { - if (!(await isCompatible(context))) { + execute: async ({ data }: ValueClickActionContext) => { + if (!(await isCompatible({ data }))) { throw new IncompatibleActionError(); } - const filters: Filter[] = await createFiltersFromValueClickAction(context.data); + const filters: Filter[] = await createFiltersFromValueClickAction(data); let selectedFilters = filters; @@ -98,9 +98,9 @@ export function valueClickAction( selectedFilters = await filterSelectionPromise; } - if (context.timeFieldName) { + if (data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - context.timeFieldName, + data.timeFieldName, selectedFilters ); filterManager.addFilters(restOfFilters); diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts index f3297f21c572a5..ecbfceac14b664 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -28,8 +28,13 @@ import { MappingObject, } from '../../../../kibana_utils/public'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; - +import { + ES_FIELD_TYPES, + KBN_FIELD_TYPES, + IIndexPattern, + IFieldType, + META_FIELDS_SETTING, +} from '../../../common'; import { findByTitle } from '../utils'; import { IndexPatternMissingIndices } from '../lib'; import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '../fields'; @@ -104,7 +109,7 @@ export class IndexPattern implements IIndexPattern { this.getConfig = getConfig; this.shortDotsEnable = this.getConfig('shortDots:enable'); - this.metaFields = this.getConfig('metaFields'); + this.metaFields = this.getConfig(META_FIELDS_SETTING); this.createFieldList = getIndexPatternFieldListCreator({ fieldFormats: getFieldFormats(), @@ -112,8 +117,8 @@ export class IndexPattern implements IIndexPattern { }); this.fields = this.createFieldList(this, [], this.shortDotsEnable); - this.fieldsFetcher = createFieldsFetcher(this, apiClient, this.getConfig('metaFields')); - this.flattenHit = flattenHitWrapper(this, this.getConfig('metaFields')); + this.fieldsFetcher = createFieldsFetcher(this, apiClient, this.getConfig(META_FIELDS_SETTING)); + this.flattenHit = flattenHitWrapper(this, this.getConfig(META_FIELDS_SETTING)); this.formatHit = formatHitProvider( this, getFieldFormats().getDefaultInstance(KBN_FIELD_TYPES.STRING) diff --git a/src/plugins/data/public/search/long_query_notification.tsx b/src/plugins/data/public/search/long_query_notification.tsx index 590fee20db690f..0bdf8ab7c66f82 100644 --- a/src/plugins/data/public/search/long_query_notification.tsx +++ b/src/plugins/data/public/search/long_query_notification.tsx @@ -44,9 +44,7 @@ export function LongQueryNotification(props: Props) { { - await props.application.navigateToApp( - 'kibana#/management/elasticsearch/license_management' - ); + await props.application.navigateToApp('kibana#/management/stack/license_management'); }} > filter(docvalueField.field) ); @@ -444,7 +448,7 @@ export class SearchSource { body.query = buildEsQuery(index, query, filters, esQueryConfigs); if (highlightAll && body.query) { - body.highlight = getHighlightRequest(body.query, uiSettings.get('doc_table:highlight')); + body.highlight = getHighlightRequest(body.query, uiSettings.get(DOC_HIGHLIGHT_SETTING)); delete searchRequest.highlightAll; } diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index efb8759e7beade..83a5358642ce42 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -27,6 +27,7 @@ import { KqlTelemetryService } from './kql_telemetry'; import { UsageCollectionSetup } from '../../usage_collection/server'; import { AutocompleteService } from './autocomplete'; import { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats'; +import { uiSettings } from './ui_settings'; export interface DataPluginSetup { search: ISearchSetup; @@ -63,6 +64,7 @@ export class DataServerPlugin implements Plugin = (context: ISearc // src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:189:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:64:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:65:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts new file mode 100644 index 00000000000000..5af62be2952011 --- /dev/null +++ b/src/plugins/data/server/ui_settings.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { UiSettingsParams } from 'kibana/server'; +import { META_FIELDS_SETTING, DOC_HIGHLIGHT_SETTING } from '../common'; + +export const uiSettings: Record = { + [META_FIELDS_SETTING]: { + name: i18n.translate('data.advancedSettings.metaFieldsTitle', { + defaultMessage: 'Meta fields', + }), + value: ['_source', '_id', '_type', '_index', '_score'], + description: i18n.translate('data.advancedSettings.metaFieldsText', { + defaultMessage: + 'Fields that exist outside of _source to merge into our document when displaying it', + }), + schema: schema.arrayOf(schema.string()), + }, + [DOC_HIGHLIGHT_SETTING]: { + name: i18n.translate('data.advancedSettings.docTableHighlightTitle', { + defaultMessage: 'Highlight results', + }), + value: true, + description: i18n.translate('data.advancedSettings.docTableHighlightText', { + defaultMessage: + 'Highlight results in Discover and Saved Searches Dashboard. ' + + 'Highlighting makes requests slow when working on big documents.', + }), + category: ['discover'], + schema: schema.boolean(), + }, +}; diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts new file mode 100644 index 00000000000000..72030d91220b5b --- /dev/null +++ b/src/plugins/discover/common/index.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const DEFAULT_COLUMNS_SETTING = 'defaultColumns'; +export const SAMPLE_SIZE_SETTING = 'discover:sampleSize'; +export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; +export const SORT_DEFAULT_ORDER_SETTING = 'discover:sort:defaultOrder'; +export const SEARCH_ON_PAGE_LOAD_SETTING = 'discover:searchOnPageLoad'; +export const DOC_HIDE_TIME_COLUMN_SETTING = 'doc_table:hideTimeColumn'; +export const FIELDS_LIMIT_SETTING = 'fields:popularLimit'; +export const CONTEXT_DEFAULT_SIZE_SETTING = 'context:defaultSize'; +export const CONTEXT_STEP_SETTING = 'context:step'; +export const CONTEXT_TIE_BREAKER_FIELDS_SETTING = 'context:tieBreakerFields'; diff --git a/src/plugins/discover/public/application/angular/context.js b/src/plugins/discover/public/application/angular/context.js index 6889eceb9ac17f..f395f28a428fcc 100644 --- a/src/plugins/discover/public/application/angular/context.js +++ b/src/plugins/discover/public/application/angular/context.js @@ -19,6 +19,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { CONTEXT_DEFAULT_SIZE_SETTING } from '../../../common'; import { getAngularModule, getServices } from '../../kibana_services'; import './context_app'; import { getState } from './context_state'; @@ -78,7 +79,7 @@ function ContextAppRouteController($routeParams, $scope, $route) { setAppState, flushToUrl, } = getState({ - defaultStepSize: getServices().uiSettings.get('context:defaultSize'), + defaultStepSize: getServices().uiSettings.get(CONTEXT_DEFAULT_SIZE_SETTING), timeFieldName: indexPattern.timeFieldName, storeInSessionStorage: getServices().uiSettings.get('state:storeInSessionStorage'), history: getServices().history(), diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index c4a42642f7fd5a..e79689d3e26e34 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -18,6 +18,7 @@ */ import _ from 'lodash'; +import { CONTEXT_STEP_SETTING, CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../common'; import { getAngularModule, getServices } from '../../kibana_services'; import contextAppTemplate from './context_app.html'; import './context/components/action_bar'; @@ -61,8 +62,8 @@ function ContextAppController($scope, Private) { const queryParameterActions = getQueryParameterActions(filterManager, indexpatterns); const queryActions = Private(QueryActionsProvider); this.state = createInitialState( - parseInt(uiSettings.get('context:step'), 10), - getFirstSortableField(this.indexPattern, uiSettings.get('context:tieBreakerFields')) + parseInt(uiSettings.get(CONTEXT_STEP_SETTING), 10), + getFirstSortableField(this.indexPattern, uiSettings.get(CONTEXT_TIE_BREAKER_FIELDS_SETTING)) ); this.actions = _.mapValues( diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 69dec63ae57ba7..e2d434074afdb1 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -75,6 +75,13 @@ import { } from '../../../../data/public'; import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; +import { + DEFAULT_COLUMNS_SETTING, + SAMPLE_SIZE_SETTING, + SORT_DEFAULT_ORDER_SETTING, + SEARCH_ON_PAGE_LOAD_SETTING, + DOC_HIDE_TIME_COLUMN_SETTING, +} from '../../../common'; const fetchStatuses = { UNINITIALIZED: 'uninitialized', @@ -554,7 +561,7 @@ function discoverController( const { searchFields, selectFields } = await getSharingDataFields( $scope.state.columns, $scope.indexPattern.timeFieldName, - config.get('doc_table:hideTimeColumn') + config.get(DOC_HIDE_TIME_COLUMN_SETTING) ); searchSource.setField('fields', searchFields); searchSource.setField( @@ -562,7 +569,7 @@ function discoverController( getSortForSearchSource( $scope.state.sort, $scope.indexPattern, - config.get('discover:sort:defaultOrder') + config.get(SORT_DEFAULT_ORDER_SETTING) ) ); searchSource.setField('highlight', null); @@ -595,7 +602,9 @@ function discoverController( query, sort: getSortArray(savedSearch.sort, $scope.indexPattern), columns: - savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(), + savedSearch.columns.length > 0 + ? savedSearch.columns + : config.get(DEFAULT_COLUMNS_SETTING).slice(), index: $scope.indexPattern.id, interval: 'auto', filters: _.cloneDeep($scope.searchSource.getOwnField('filter')), @@ -625,7 +634,7 @@ function discoverController( $scope.opts = { // number of records to fetch, then paginate through - sampleSize: config.get('discover:sampleSize'), + sampleSize: config.get(SAMPLE_SIZE_SETTING), timefield: getTimeField(), savedSearch: savedSearch, indexPatternList: $route.current.locals.savedObjects.ip.list, @@ -635,7 +644,7 @@ function discoverController( // A saved search is created on every page load, so we check the ID to see if we're loading a // previously saved search or if it is just transient return ( - config.get('discover:searchOnPageLoad') || + config.get(SEARCH_ON_PAGE_LOAD_SETTING) || savedSearch.id !== undefined || timefilter.getRefreshInterval().pause === false ); @@ -946,7 +955,7 @@ function discoverController( getSortForSearchSource( $scope.state.sort, indexPattern, - config.get('discover:sort:defaultOrder') + config.get(SORT_DEFAULT_ORDER_SETTING) ) ) .setField('query', $scope.state.query || null) diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts index 5e7ad6a1d1ea0b..60dfb69e85e74b 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts @@ -18,6 +18,7 @@ */ import { TableHeader } from './table_header/table_header'; import { getServices } from '../../../../kibana_services'; +import { SORT_DEFAULT_ORDER_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; export function createTableHeaderDirective(reactDirective: any) { const { uiSettings: config } = getServices(); @@ -36,9 +37,9 @@ export function createTableHeaderDirective(reactDirective: any) { ], { restrict: 'A' }, { - hideTimeColumn: config.get('doc_table:hideTimeColumn'), + hideTimeColumn: config.get(DOC_HIDE_TIME_COLUMN_SETTING, false), isShortDots: config.get('shortDots:enable'), - defaultSortOrder: config.get('discover:sort:defaultOrder'), + defaultSortOrder: config.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), } ); } diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index 5fa37a5ac28eb0..fbbbabdbe7bc28 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -29,6 +29,7 @@ import openRowHtml from './table_row/open.html'; import detailsHtml from './table_row/details.html'; import { dispatchRenderComplete } from '../../../../../../kibana_utils/public'; +import { DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; import { esFilters } from '../../../../../../data/public'; @@ -137,7 +138,7 @@ export function createTableRowDirective($compile: ng.ICompileService, $httpParam const newHtmls = [openRowHtml]; const mapping = indexPattern.fields.getByName; - const hideTimeColumn = getServices().uiSettings.get('doc_table:hideTimeColumn'); + const hideTimeColumn = getServices().uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); if (indexPattern.timeFieldName && !hideTimeColumn) { newHtmls.push( cellTemplate({ diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts index 8af7380afcdc97..70f277543a52ae 100644 --- a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts +++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts @@ -19,6 +19,7 @@ import html from './doc_table.html'; import { dispatchRenderComplete } from '../../../../../kibana_utils/public'; +import { SAMPLE_SIZE_SETTING } from '../../../../common'; // @ts-ignore import { getLimitedSearchResultsMessage } from './doc_table_strings'; import { getServices } from '../../../kibana_services'; @@ -65,7 +66,7 @@ export function createDocTableDirective(pagerFactory: any, $filter: any) { }; $scope.limitedResultsWarning = getLimitedSearchResultsMessage( - getServices().uiSettings.get('discover:sampleSize') + getServices().uiSettings.get(SAMPLE_SIZE_SETTING, 500) ); $scope.addRows = function() { diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index 56597dd31e572c..0d9aebe11ece60 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -27,6 +27,7 @@ import { DiscoverIndexPattern } from './discover_index_pattern'; import { DiscoverFieldSearch } from './discover_field_search'; import { IndexPatternAttributes } from '../../../../../data/common'; import { SavedObject } from '../../../../../../core/types'; +import { FIELDS_LIMIT_SETTING } from '../../../../common'; import { groupFields } from './lib/group_fields'; import { IIndexPatternFieldList, @@ -131,7 +132,7 @@ export function DiscoverSidebar({ [selectedIndexPattern, state, columns, hits, services] ); - const popularLimit = services.uiSettings.get('fields:popularLimit'); + const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING); const useShortDots = services.uiSettings.get('shortDots:enable'); const { diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts index 6b2d20ae8c9108..d585c5d6f26382 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts @@ -28,6 +28,7 @@ import { import { AppState } from '../../../angular/discover_state'; import { DiscoverServices } from '../../../../build_services'; import { VisualizationsStart, VisTypeAlias } from '../../../../../../visualizations/public'; +import { AGGS_TERMS_SIZE_SETTING } from '../../../../../common'; export function isMapsAppRegistered(visualizations: VisualizationsStart) { return visualizations.getAliases().some(({ name }: VisTypeAlias) => { @@ -114,7 +115,7 @@ export function getVisualizeUrl( columns: string[], services: DiscoverServices ) { - const aggsTermSize = services.uiSettings.get('discover:aggs:terms:size'); + const aggsTermSize = services.uiSettings.get(AGGS_TERMS_SIZE_SETTING); const urlParams = parse(services.history().location.search) as Record; if ( diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts index f1bda5fcae10b2..ed70c90eb64e6d 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts @@ -48,6 +48,7 @@ import { } from '../../kibana_services'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; import { SavedSearch } from '../..'; +import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; interface SearchScope extends ng.IScope { columns?: string[]; @@ -273,13 +274,13 @@ export class SearchEmbeddable extends Embeddable if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); - searchSource.setField('size', getServices().uiSettings.get('discover:sampleSize')); + searchSource.setField('size', getServices().uiSettings.get(SAMPLE_SIZE_SETTING)); searchSource.setField( 'sort', getSortForSearchSource( this.searchScope.sort, this.searchScope.indexPattern, - getServices().uiSettings.get('discover:sort:defaultOrder') + getServices().uiSettings.get(SORT_DEFAULT_ORDER_SETTING) ) ); diff --git a/src/plugins/discover/server/index.ts b/src/plugins/discover/server/index.ts index 15a948c56148ef..20ccc8b1820785 100644 --- a/src/plugins/discover/server/index.ts +++ b/src/plugins/discover/server/index.ts @@ -17,8 +17,6 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/server'; import { DiscoverServerPlugin } from './plugin'; -export const plugin = (initContext: PluginInitializerContext) => - new DiscoverServerPlugin(initContext); +export const plugin = () => new DiscoverServerPlugin(); diff --git a/src/plugins/discover/server/plugin.ts b/src/plugins/discover/server/plugin.ts index 04502f5fc14e60..a7445a51891635 100644 --- a/src/plugins/discover/server/plugin.ts +++ b/src/plugins/discover/server/plugin.ts @@ -17,26 +17,19 @@ * under the License. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'kibana/server'; +import { CoreSetup, CoreStart, Plugin } from 'kibana/server'; +import { uiSettings } from './ui_settings'; import { capabilitiesProvider } from './capabilities_provider'; export class DiscoverServerPlugin implements Plugin { - private readonly logger: Logger; - - constructor(initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get(); - } - public setup(core: CoreSetup) { - this.logger.debug('discover: Setup'); - core.capabilities.registerProvider(capabilitiesProvider); + core.uiSettings.register(uiSettings); return {}; } public start(core: CoreStart) { - this.logger.debug('discover: Started'); return {}; } diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts new file mode 100644 index 00000000000000..3eca11cc584a91 --- /dev/null +++ b/src/plugins/discover/server/ui_settings.ts @@ -0,0 +1,166 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { UiSettingsParams } from 'kibana/server'; +import { + DEFAULT_COLUMNS_SETTING, + SAMPLE_SIZE_SETTING, + AGGS_TERMS_SIZE_SETTING, + SORT_DEFAULT_ORDER_SETTING, + SEARCH_ON_PAGE_LOAD_SETTING, + DOC_HIDE_TIME_COLUMN_SETTING, + FIELDS_LIMIT_SETTING, + CONTEXT_DEFAULT_SIZE_SETTING, + CONTEXT_STEP_SETTING, + CONTEXT_TIE_BREAKER_FIELDS_SETTING, +} from '../common'; + +export const uiSettings: Record = { + [DEFAULT_COLUMNS_SETTING]: { + name: i18n.translate('discover.advancedSettings.defaultColumnsTitle', { + defaultMessage: 'Default columns', + }), + value: ['_source'], + description: i18n.translate('discover.advancedSettings.defaultColumnsText', { + defaultMessage: 'Columns displayed by default in the Discovery tab', + }), + category: ['discover'], + schema: schema.arrayOf(schema.string()), + }, + [SAMPLE_SIZE_SETTING]: { + name: i18n.translate('discover.advancedSettings.sampleSizeTitle', { + defaultMessage: 'Number of rows', + }), + value: 500, + description: i18n.translate('discover.advancedSettings.sampleSizeText', { + defaultMessage: 'The number of rows to show in the table', + }), + category: ['discover'], + schema: schema.number(), + }, + [AGGS_TERMS_SIZE_SETTING]: { + name: i18n.translate('discover.advancedSettings.aggsTermsSizeTitle', { + defaultMessage: 'Number of terms', + }), + value: 20, + type: 'number', + description: i18n.translate('discover.advancedSettings.aggsTermsSizeText', { + defaultMessage: + 'Determines how many terms will be visualized when clicking the "visualize" ' + + 'button, in the field drop downs, in the discover sidebar.', + }), + category: ['discover'], + schema: schema.number(), + }, + [SORT_DEFAULT_ORDER_SETTING]: { + name: i18n.translate('discover.advancedSettings.sortDefaultOrderTitle', { + defaultMessage: 'Default sort direction', + }), + value: 'desc', + options: ['desc', 'asc'], + optionLabels: { + desc: i18n.translate('discover.advancedSettings.sortOrderDesc', { + defaultMessage: 'Descending', + }), + asc: i18n.translate('discover.advancedSettings.sortOrderAsc', { + defaultMessage: 'Ascending', + }), + }, + type: 'select', + description: i18n.translate('discover.advancedSettings.sortDefaultOrderText', { + defaultMessage: + 'Controls the default sort direction for time based index patterns in the Discover app.', + }), + category: ['discover'], + schema: schema.oneOf([schema.literal('desc'), schema.literal('asc')]), + }, + [SEARCH_ON_PAGE_LOAD_SETTING]: { + name: i18n.translate('discover.advancedSettings.searchOnPageLoadTitle', { + defaultMessage: 'Search on page load', + }), + value: true, + type: 'boolean', + description: i18n.translate('discover.advancedSettings.searchOnPageLoadText', { + defaultMessage: + 'Controls whether a search is executed when Discover first loads. This setting does not ' + + 'have an effect when loading a saved search.', + }), + category: ['discover'], + schema: schema.boolean(), + }, + [DOC_HIDE_TIME_COLUMN_SETTING]: { + name: i18n.translate('discover.advancedSettings.docTableHideTimeColumnTitle', { + defaultMessage: "Hide 'Time' column", + }), + value: false, + description: i18n.translate('discover.advancedSettings.docTableHideTimeColumnText', { + defaultMessage: "Hide the 'Time' column in Discover and in all Saved Searches on Dashboards.", + }), + category: ['discover'], + schema: schema.boolean(), + }, + [FIELDS_LIMIT_SETTING]: { + name: i18n.translate('discover.advancedSettings.fieldsPopularLimitTitle', { + defaultMessage: 'Popular fields limit', + }), + value: 10, + description: i18n.translate('discover.advancedSettings.fieldsPopularLimitText', { + defaultMessage: 'The top N most popular fields to show', + }), + schema: schema.number(), + }, + [CONTEXT_DEFAULT_SIZE_SETTING]: { + name: i18n.translate('discover.advancedSettings.context.defaultSizeTitle', { + defaultMessage: 'Context size', + }), + value: 5, + description: i18n.translate('discover.advancedSettings.context.defaultSizeText', { + defaultMessage: 'The number of surrounding entries to show in the context view', + }), + category: ['discover'], + schema: schema.number(), + }, + [CONTEXT_STEP_SETTING]: { + name: i18n.translate('discover.advancedSettings.context.sizeStepTitle', { + defaultMessage: 'Context size step', + }), + value: 5, + description: i18n.translate('discover.advancedSettings.context.sizeStepText', { + defaultMessage: 'The step size to increment or decrement the context size by', + }), + category: ['discover'], + schema: schema.number(), + }, + [CONTEXT_TIE_BREAKER_FIELDS_SETTING]: { + name: i18n.translate('discover.advancedSettings.context.tieBreakerFieldsTitle', { + defaultMessage: 'Tie breaker fields', + }), + value: ['_doc'], + description: i18n.translate('discover.advancedSettings.context.tieBreakerFieldsText', { + defaultMessage: + 'A comma-separated list of fields to use for tie-breaking between documents that have the same timestamp value. ' + + 'From this list the first field that is present and sortable in the current index pattern is used.', + }), + category: ['discover'], + schema: schema.arrayOf(schema.string()), + }, +}; diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index c097e3e8c13be8..2b447c89e28501 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -27,7 +27,6 @@ export interface EmbeddableContext { export interface ValueClickTriggerContext { embeddable?: T; - timeFieldName?: string; data: { data: Array<{ table: Pick; @@ -35,6 +34,7 @@ export interface ValueClickTriggerContext { row: number; value: any; }>; + timeFieldName?: string; negate?: boolean; }; } @@ -45,11 +45,11 @@ export const isValueClickTriggerContext = ( export interface RangeSelectTriggerContext { embeddable?: T; - timeFieldName?: string; data: { table: KibanaDatatable; column: number; range: number[]; + timeFieldName?: string; }; } diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 4c2dac4f39134c..58003a7e9e8419 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -37,11 +37,9 @@ import { EuiCallOut, EuiBasicTableColumn, } from '@elastic/eui'; -import { ToastsStart, IUiSettingsClient } from 'kibana/public'; +import { ToastsStart } from 'kibana/public'; import { toMountPoint } from '../util'; -export const EMPTY_FILTER = ''; - interface Column { name: string; width?: string; @@ -61,12 +59,12 @@ export interface TableListViewProps { findItems(query: string): Promise<{ total: number; hits: object[] }>; listingLimit: number; initialFilter: string; + initialPageSize: number; noItemsFragment: JSX.Element; // update possible column types to something like (FieldDataColumn | ComputedColumn | ActionsColumn)[] when they have been added to EUI tableColumns: Column[]; tableListTitle: string; toastNotifications: ToastsStart; - uiSettings: IUiSettingsClient; /** * Id of the heading element describing the table. This id will be used as `aria-labelledby` of the wrapper element. * If the table is not empty, this component renders its own h1 element using the same id. @@ -98,11 +96,10 @@ class TableListView extends React.Component { + this.main.register(id, { + display: title, + order: idx, + }); }); return this.main; diff --git a/src/plugins/management/public/management_section.test.ts b/src/plugins/management/public/management_section.test.ts index c68175ee0a678b..e1d047425ac180 100644 --- a/src/plugins/management/public/management_section.test.ts +++ b/src/plugins/management/public/management_section.test.ts @@ -18,6 +18,7 @@ */ import { ManagementSection } from './management_section'; +import { ManagementSectionId } from './types'; // @ts-ignore import { LegacyManagementSection } from './legacy'; import { coreMock } from '../../../core/public/mocks'; @@ -27,7 +28,7 @@ function createSection(registerLegacyApp: () => void) { const getLegacySection = () => legacySection; const getManagementSections: () => ManagementSection[] = () => []; - const testSectionConfig = { id: 'test-section', title: 'Test Section' }; + const testSectionConfig = { id: ManagementSectionId.Data, title: 'Test Section' }; return new ManagementSection( testSectionConfig, getManagementSections, diff --git a/src/plugins/management/public/management_section.ts b/src/plugins/management/public/management_section.ts index 483605341ae4c4..ace8f87bec7667 100644 --- a/src/plugins/management/public/management_section.ts +++ b/src/plugins/management/public/management_section.ts @@ -17,7 +17,9 @@ * under the License. */ -import { CreateSection, RegisterManagementAppArgs } from './types'; +import { ReactElement } from 'react'; + +import { CreateSection, RegisterManagementAppArgs, ManagementSectionId } from './types'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; import { StartServicesAccessor } from '../../../core/public'; // @ts-ignore @@ -25,8 +27,8 @@ import { LegacyManagementSection } from './legacy'; import { ManagementApp } from './management_app'; export class ManagementSection { - public readonly id: string = ''; - public readonly title: string = ''; + public readonly id: ManagementSectionId; + public readonly title: string | ReactElement = ''; public readonly apps: ManagementApp[] = []; public readonly order: number; public readonly euiIconType?: string; diff --git a/src/plugins/management/public/management_sections.tsx b/src/plugins/management/public/management_sections.tsx new file mode 100644 index 00000000000000..77e494626a00ee --- /dev/null +++ b/src/plugins/management/public/management_sections.tsx @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiIcon } from '@elastic/eui'; + +import { ManagementSectionId } from './types'; + +interface Props { + text: string; + tip: string; +} + +const ManagementSectionTitle = ({ text, tip }: Props) => ( + + + {text} + + + + + + +); + +export const managementSections = [ + { + id: ManagementSectionId.Ingest, + title: ( + + ), + }, + { + id: ManagementSectionId.Data, + title: , + }, + { + id: ManagementSectionId.InsightsAndAlerting, + title: ( + + ), + }, + { + id: ManagementSectionId.Security, + title: , + }, + { + id: ManagementSectionId.Kibana, + title: , + }, + { + id: ManagementSectionId.Stack, + title: , + }, +]; diff --git a/src/plugins/management/public/management_service.test.ts b/src/plugins/management/public/management_service.test.ts index 18569ef285ff3a..1507d6f43619dc 100644 --- a/src/plugins/management/public/management_service.test.ts +++ b/src/plugins/management/public/management_service.test.ts @@ -18,6 +18,7 @@ */ import { ManagementService } from './management_service'; +import { ManagementSectionId } from './types'; import { coreMock } from '../../../core/public/mocks'; import { npSetup } from '../../../legacy/ui/public/new_platform/__mocks__'; @@ -29,27 +30,11 @@ test('Provides default sections', () => { () => {}, coreMock.createSetup().getStartServices ); - expect(service.getAllSections().length).toEqual(2); - expect(service.getSection('kibana')).not.toBeUndefined(); - expect(service.getSection('elasticsearch')).not.toBeUndefined(); -}); - -test('Register section, enable and disable', () => { - const service = new ManagementService().setup( - npSetup.plugins.kibanaLegacy, - () => {}, - coreMock.createSetup().getStartServices - ); - const testSection = service.register({ id: 'test-section', title: 'Test Section' }); - expect(service.getSection('test-section')).not.toBeUndefined(); - - const testApp = testSection.registerApp({ - id: 'test-app', - title: 'Test App', - mount: () => () => {}, - }); - expect(testSection.getApp('test-app')).not.toBeUndefined(); - expect(service.getSectionsEnabled().length).toEqual(1); - testApp.disable(); - expect(service.getSectionsEnabled().length).toEqual(0); + expect(service.getAllSections().length).toEqual(6); + expect(service.getSection(ManagementSectionId.Ingest)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Data)).toBeDefined(); + expect(service.getSection(ManagementSectionId.InsightsAndAlerting)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Security)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Kibana)).toBeDefined(); + expect(service.getSection(ManagementSectionId.Stack)).toBeDefined(); }); diff --git a/src/plugins/management/public/management_service.ts b/src/plugins/management/public/management_service.ts index 8fc207e32e6ce7..85d27a526d4026 100644 --- a/src/plugins/management/public/management_service.ts +++ b/src/plugins/management/public/management_service.ts @@ -17,11 +17,14 @@ * under the License. */ +import { ReactElement } from 'react'; + import { ManagementSection } from './management_section'; +import { managementSections } from './management_sections'; import { KibanaLegacySetup } from '../../kibana_legacy/public'; // @ts-ignore -import { LegacyManagementSection } from './legacy'; -import { CreateSection } from './types'; +import { LegacyManagementSection, sections } from './legacy'; +import { CreateSection, ManagementSectionId } from './types'; import { StartServicesAccessor, CoreStart } from '../../../core/public'; export class ManagementService { @@ -48,7 +51,8 @@ export class ManagementService { return newSection; }; } - private getSection(sectionId: ManagementSection['id']) { + + private getSection(sectionId: ManagementSectionId) { return this.sections.find(section => section.id === sectionId); } @@ -63,7 +67,13 @@ export class ManagementService { } private sharedInterface = { - getSection: this.getSection.bind(this), + getSection: (sectionId: ManagementSectionId) => { + const section = this.getSection(sectionId); + if (!section) { + throw new Error(`Management section with id ${sectionId} is undefined`); + } + return section; + }, getSectionsEnabled: this.getSectionsEnabled.bind(this), getAllSections: this.getAllSections.bind(this), }; @@ -79,16 +89,13 @@ export class ManagementService { getStartServices ); - register({ id: 'kibana', title: 'Kibana', order: 30, euiIconType: 'logoKibana' }); - register({ - id: 'elasticsearch', - title: 'Elasticsearch', - order: 20, - euiIconType: 'logoElasticsearch', - }); + managementSections.forEach( + ({ id, title }: { id: ManagementSectionId; title: ReactElement }, idx: number) => { + register({ id, title, order: idx }); + } + ); return { - register, ...this.sharedInterface, }; } diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index 82789d3c3f55f1..3e32ff4fe26b29 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -30,7 +30,6 @@ const createManagementSectionMock = (): jest.Mocked => ({ sections: { - register: jest.fn(), getSection: jest.fn().mockReturnValue(createManagementSectionMock()), getAllSections: jest.fn().mockReturnValue([]), }, diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index a8bdd5cca24a32..ecd727e8703ff5 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -17,6 +17,26 @@ * under the License. */ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the 'License'); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ReactElement } from 'react'; import { IconType } from '@elastic/eui'; import { ManagementApp } from './management_app'; import { ManagementSection } from './management_section'; @@ -31,21 +51,29 @@ export interface ManagementStart { legacy: any; } +export enum ManagementSectionId { + Ingest = 'ingest', + Data = 'data', + InsightsAndAlerting = 'insightsAndAlerting', + Security = 'security', + Kibana = 'kibana', + Stack = 'stack', +} + interface SectionsServiceSetup { - getSection: (sectionId: ManagementSection['id']) => ManagementSection | undefined; + getSection: (sectionId: ManagementSectionId) => ManagementSection; getAllSections: () => ManagementSection[]; - register: RegisterSection; } interface SectionsServiceStart { - getSection: (sectionId: ManagementSection['id']) => ManagementSection | undefined; + getSection: (sectionId: ManagementSectionId) => ManagementSection; getAllSections: () => ManagementSection[]; navigateToApp: ApplicationStart['navigateToApp']; } export interface CreateSection { - id: string; - title: string; + id: ManagementSectionId; + title: string | ReactElement; order?: number; euiIconType?: string; // takes precedence over `icon` property. icon?: string; // URL to image file; fallback if no `euiIconType` diff --git a/src/plugins/saved_objects/common/index.ts b/src/plugins/saved_objects/common/index.ts new file mode 100644 index 00000000000000..ef376e80cab073 --- /dev/null +++ b/src/plugins/saved_objects/common/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const PER_PAGE_SETTING = 'savedObjects:perPage'; +export const LISTING_LIMIT_SETTING = 'savedObjects:listingLimit'; diff --git a/src/plugins/saved_objects/kibana.json b/src/plugins/saved_objects/kibana.json index 0792955b7c5f11..7ae1b84eecad80 100644 --- a/src/plugins/saved_objects/kibana.json +++ b/src/plugins/saved_objects/kibana.json @@ -1,7 +1,7 @@ { "id": "savedObjects", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": ["data"] } diff --git a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx index 81600e9f686345..5b5242579c718e 100644 --- a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx +++ b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx @@ -49,6 +49,8 @@ import { SavedObjectsStart, } from 'src/core/public'; +import { LISTING_LIMIT_SETTING } from '../../common'; + export interface SavedObjectMetaData { type: string; name: string; @@ -131,7 +133,7 @@ class SavedObjectFinderUi extends React.Component< .map(metaData => metaData.includeFields || []) .reduce((allFields, currentFields) => allFields.concat(currentFields), ['title']); - const perPage = this.props.uiSettings.get('savedObjects:listingLimit'); + const perPage = this.props.uiSettings.get(LISTING_LIMIT_SETTING); const resp = await this.props.savedObjects.client.find({ type: Object.keys(metaDataMap), fields: [...new Set(fields)], diff --git a/src/plugins/saved_objects/public/index.ts b/src/plugins/saved_objects/public/index.ts index e38a0ef9830eae..4f7a4ff7f196f5 100644 --- a/src/plugins/saved_objects/public/index.ts +++ b/src/plugins/saved_objects/public/index.ts @@ -36,5 +36,6 @@ export { isErrorNonFatal, } from './saved_object'; export { SavedObjectSaveOpts, SavedObjectKibanaServices, SavedObject } from './types'; +export { SavedObjectsStart } from './plugin'; export const plugin = () => new SavedObjectsPublicPlugin(); diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index 7927238e120663..d430c8896484da 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -22,9 +22,14 @@ import { CoreStart, Plugin } from 'src/core/public'; import './index.scss'; import { createSavedObjectClass } from './saved_object'; import { DataPublicPluginStart } from '../../data/public'; +import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; export interface SavedObjectsStart { SavedObjectClass: any; + settings: { + getPerPage: () => number; + getListingLimit: () => number; + }; } export interface SavedObjectsStartDeps { @@ -43,6 +48,10 @@ export class SavedObjectsPublicPlugin chrome: core.chrome, overlays: core.overlays, }), + settings: { + getPerPage: () => core.uiSettings.get(PER_PAGE_SETTING), + getListingLimit: () => core.uiSettings.get(LISTING_LIMIT_SETTING), + }, }; } } diff --git a/src/plugins/saved_objects/server/index.ts b/src/plugins/saved_objects/server/index.ts new file mode 100644 index 00000000000000..4e72bfef0637e7 --- /dev/null +++ b/src/plugins/saved_objects/server/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsServerPlugin } from './plugin'; + +export const plugin = () => new SavedObjectsServerPlugin(); diff --git a/packages/kbn-plugin-helpers/lib/index.d.ts b/src/plugins/saved_objects/server/plugin.ts similarity index 71% rename from packages/kbn-plugin-helpers/lib/index.d.ts rename to src/plugins/saved_objects/server/plugin.ts index 6f1eb73505b05e..9a656fcb510ac8 100644 --- a/packages/kbn-plugin-helpers/lib/index.d.ts +++ b/src/plugins/saved_objects/server/plugin.ts @@ -17,10 +17,18 @@ * under the License. */ -export function babelRegister(): void; -export function resolveKibanaPath(path: string): string; -export function readFtrConfigFile(path: string): any; -export function run( - task: 'build' | 'start' | 'testAll' | 'testKarma' | 'testMocha' | 'postinstall', - options: any -): Promise; +import { CoreSetup, Plugin } from 'kibana/server'; +import { uiSettings } from './ui_settings'; + +export class SavedObjectsServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.uiSettings.register(uiSettings); + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/saved_objects/server/ui_settings.ts b/src/plugins/saved_objects/server/ui_settings.ts new file mode 100644 index 00000000000000..2e2e00b2c3ca74 --- /dev/null +++ b/src/plugins/saved_objects/server/ui_settings.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { UiSettingsParams } from 'kibana/server'; +import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common'; + +export const uiSettings: Record = { + [PER_PAGE_SETTING]: { + name: i18n.translate('savedObjects.advancedSettings.perPageTitle', { + defaultMessage: 'Objects per page', + }), + value: 20, + type: 'number', + description: i18n.translate('savedObjects.advancedSettings.perPageText', { + defaultMessage: 'Number of objects to show per page in the load dialog', + }), + schema: schema.number(), + }, + [LISTING_LIMIT_SETTING]: { + name: i18n.translate('savedObjects.advancedSettings.listingLimitTitle', { + defaultMessage: 'Objects listing limit', + }), + type: 'number', + value: 1000, + description: i18n.translate('savedObjects.advancedSettings.listingLimitText', { + defaultMessage: 'Number of objects to fetch for the listing pages', + }), + schema: schema.number(), + }, +}; diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts index e7d6754ac4d05a..7c447aefbc69ac 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.test.ts @@ -59,6 +59,11 @@ describe('createFieldList', () => { "type": "boolean", "value": true, }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); @@ -76,6 +81,11 @@ describe('createFieldList', () => { \\"data\\": \\"value\\" }", }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); @@ -93,6 +103,48 @@ describe('createFieldList', () => { 1, 2, 3 + ]", + }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, + ] + `); + }); + + it(`generates a field for the object's references`, () => { + const obj = createObject( + { + someString: 'foo', + }, + [ + { id: 'ref1', type: 'type', name: 'Ref 1' }, + { id: 'ref12', type: 'other-type', name: 'Ref 2' }, + ] + ); + expect(createFieldList(obj)).toMatchInlineSnapshot(` + Array [ + Object { + "name": "someString", + "type": "text", + "value": "foo", + }, + Object { + "name": "references", + "type": "array", + "value": "[ + { + \\"id\\": \\"ref1\\", + \\"type\\": \\"type\\", + \\"name\\": \\"Ref 1\\" + }, + { + \\"id\\": \\"ref12\\", + \\"type\\": \\"other-type\\", + \\"name\\": \\"Ref 2\\" + } ]", }, ] @@ -126,6 +178,11 @@ describe('createFieldList', () => { "type": "text", "value": "B", }, + Object { + "name": "references", + "type": "array", + "value": "[]", + }, ] `); }); diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts index 5d87c11a871982..d66d0b0a288445 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts @@ -29,12 +29,15 @@ export function createFieldList( object: SimpleSavedObject, service?: SavedObjectLoader ): ObjectField[] { - const fields = Object.entries(object.attributes as Record).reduce( + let fields = Object.entries(object.attributes as Record).reduce( (objFields, [key, value]) => { - return [...objFields, ...recursiveCreateFields(key, value)]; + return [...objFields, ...createFields(key, value)]; }, [] as ObjectField[] ); + // Special handling for references which isn't within "attributes" + fields = [...fields, ...createFields('references', object.references)]; + if (service && (service as any).Class) { addFieldsFromClass((service as any).Class, fields); } @@ -53,7 +56,7 @@ export function createFieldList( * @param {array} parents The parent keys to the field * @returns {array} */ -const recursiveCreateFields = (key: string, value: any, parents: string[] = []): ObjectField[] => { +const createFields = (key: string, value: any, parents: string[] = []): ObjectField[] => { const path = [...parents, key]; if (path.length > maxRecursiveIterations) { return []; @@ -78,7 +81,7 @@ const recursiveCreateFields = (key: string, value: any, parents: string[] = []): } else if (isPlainObject(field.value)) { let fields: ObjectField[] = []; forOwn(field.value, (childValue, childKey) => { - fields = [...fields, ...recursiveCreateFields(childKey as string, childValue, path)]; + fields = [...fields, ...createFields(childKey as string, childValue, path)]; }); return fields; } diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx index 1b69eb4240d680..afed6b492dc91f 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx @@ -120,6 +120,7 @@ export class Field extends PureComponent { return (
{ set(source, field.name, value); }); + // we extract the `references` field that does not belong to attributes const { references, ...attributes } = source; await onSave({ attributes, references }); diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index 28eac96dcbf46e..b0c6b1952a2a5d 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { ManagementSetup } from '../../management/public'; +import { ManagementSetup, ManagementSectionId } from '../../management/public'; import { DataPublicPluginStart } from '../../data/public'; import { DashboardStart } from '../../dashboard/public'; import { DiscoverStart } from '../../discover/public'; @@ -87,16 +87,13 @@ export class SavedObjectsManagementPlugin category: FeatureCatalogueCategory.ADMIN, }); - const kibanaSection = management.sections.getSection('kibana'); - if (!kibanaSection) { - throw new Error('`kibana` management section not found.'); - } + const kibanaSection = management.sections.getSection(ManagementSectionId.Kibana); kibanaSection.registerApp({ id: 'objects', title: i18n.translate('savedObjectsManagement.managementSectionLabel', { defaultMessage: 'Saved Objects', }), - order: 10, + order: 1, mount: async mountParams => { const { mountManagementSection } = await import('./management_section'); return mountManagementSection({ diff --git a/src/plugins/telemetry_management_section/public/index.ts b/src/plugins/telemetry_management_section/public/index.ts index 6a80cdd98b1a3d..082f68809a67e5 100644 --- a/src/plugins/telemetry_management_section/public/index.ts +++ b/src/plugins/telemetry_management_section/public/index.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +export { OptInExampleFlyout } from './components'; import { TelemetryManagementSectionPlugin } from './plugin'; export function plugin() { diff --git a/src/plugins/vis_type_timeseries/common/constants.ts b/src/plugins/vis_type_timeseries/common/constants.ts new file mode 100644 index 00000000000000..fc402d6ab7db56 --- /dev/null +++ b/src/plugins/vis_type_timeseries/common/constants.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const MAX_BUCKETS_SETTING = 'metrics:max_buckets'; diff --git a/src/plugins/vis_type_timeseries/public/request_handler.js b/src/plugins/vis_type_timeseries/public/request_handler.js index bd6c6d95539309..e33d0e254f609f 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.js +++ b/src/plugins/vis_type_timeseries/public/request_handler.js @@ -19,6 +19,7 @@ import { getTimezone, validateInterval } from './application'; import { getUISettings, getDataStart, getCoreStart } from './services'; +import { MAX_BUCKETS_SETTING } from '../common/constants'; export const metricsRequestHandler = async ({ uiState, @@ -37,7 +38,7 @@ export const metricsRequestHandler = async ({ if (visParams && visParams.id && !visParams.isModelInvalid) { try { - const maxBuckets = config.get('metrics:max_buckets'); + const maxBuckets = config.get(MAX_BUCKETS_SETTING); validateInterval(parsedTimeRange, visParams, maxBuckets); diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_type_timeseries/server/plugin.ts index 05257cb79a75c8..d863937a4e3dcb 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_type_timeseries/server/plugin.ts @@ -37,6 +37,7 @@ import { visDataRoutes } from './routes/vis'; // @ts-ignore import { fieldsRoutes } from './routes/fields'; import { SearchStrategyRegistry } from './lib/search_strategies'; +import { uiSettings } from './ui_settings'; export interface LegacySetup { server: Server; @@ -75,6 +76,7 @@ export class VisTypeTimeseriesPlugin implements Plugin { public setup(core: CoreSetup, plugins: VisTypeTimeseriesPluginSetupDependencies) { const logger = this.initializerContext.logger.get('visTypeTimeseries'); + core.uiSettings.register(uiSettings); const config$ = this.initializerContext.config.create(); // Global config contains things like the ES shard timeout const globalConfig$ = this.initializerContext.config.legacy.globalConfig$; diff --git a/src/plugins/vis_type_timeseries/server/ui_settings.ts b/src/plugins/vis_type_timeseries/server/ui_settings.ts new file mode 100644 index 00000000000000..65bc166a47104c --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/ui_settings.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { UiSettingsParams } from 'kibana/server'; + +import { MAX_BUCKETS_SETTING } from '../common/constants'; + +export const uiSettings: Record = { + [MAX_BUCKETS_SETTING]: { + name: i18n.translate('visTypeTimeseries.advancedSettings.maxBucketsTitle', { + defaultMessage: 'Maximum buckets', + }), + value: 2000, + description: i18n.translate('visTypeTimeseries.advancedSettings.maxBucketsText', { + defaultMessage: 'The maximum number of buckets a single datasource can return', + }), + schema: schema.number(), + }, +}; diff --git a/packages/kbn-plugin-helpers/lib/win_cmd.js b/src/plugins/vis_type_vislib/common/index.ts similarity index 84% rename from packages/kbn-plugin-helpers/lib/win_cmd.js rename to src/plugins/vis_type_vislib/common/index.ts index 0bc672bed67c6b..e497253761a719 100644 --- a/packages/kbn-plugin-helpers/lib/win_cmd.js +++ b/src/plugins/vis_type_vislib/common/index.ts @@ -17,8 +17,5 @@ * under the License. */ -const platform = require('os').platform(); - -module.exports = function winCmd(cmd) { - return /^win/.test(platform) ? cmd + '.cmd' : cmd; -}; +export const DIMMING_OPACITY_SETTING = 'visualization:dimmingOpacity'; +export const HEATMAP_MAX_BUCKETS_SETTING = 'visualization:heatmap:maxBuckets'; diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js index b36ba336dbfe57..09256232860bc0 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js @@ -20,6 +20,7 @@ import d3 from 'd3'; import { get, pull, restParam, size, reduce } from 'lodash'; import $ from 'jquery'; +import { DIMMING_OPACITY_SETTING } from '../../../common'; /** * Handles event responses @@ -393,7 +394,7 @@ export class Dispatch { return function highlight(element) { const label = this.getAttribute('data-label'); if (!label) return; - const dimming = uiSettings.get('visualization:dimmingOpacity'); + const dimming = uiSettings.get(DIMMING_OPACITY_SETTING); $(element) .parent() .find('[data-label]') diff --git a/src/plugins/vis_type_vislib/public/vislib/vis.js b/src/plugins/vis_type_vislib/public/vislib/vis.js index ca4f36f0ab0f79..f258cb55ba2819 100644 --- a/src/plugins/vis_type_vislib/public/vislib/vis.js +++ b/src/plugins/vis_type_vislib/public/vislib/vis.js @@ -24,6 +24,7 @@ import { EventEmitter } from 'events'; import { VislibError } from './errors'; import { VisConfig } from './lib/vis_config'; import { Handler } from './lib/handler'; +import { DIMMING_OPACITY_SETTING, HEATMAP_MAX_BUCKETS_SETTING } from '../../common'; /** * Creates the visualizations. @@ -38,8 +39,8 @@ export class Vis extends EventEmitter { super(); this.element = element.get ? element.get(0) : element; this.visConfigArgs = _.cloneDeep(visConfigArgs); - this.visConfigArgs.dimmingOpacity = deps.uiSettings.get('visualization:dimmingOpacity'); - this.visConfigArgs.heatmapMaxBuckets = deps.uiSettings.get('visualization:heatmap:maxBuckets'); + this.visConfigArgs.dimmingOpacity = deps.uiSettings.get(DIMMING_OPACITY_SETTING); + this.visConfigArgs.heatmapMaxBuckets = deps.uiSettings.get(HEATMAP_MAX_BUCKETS_SETTING); this.deps = deps; } diff --git a/src/plugins/vis_type_vislib/server/index.ts b/src/plugins/vis_type_vislib/server/index.ts index 355c01d255ce77..c201dd3ddc49aa 100644 --- a/src/plugins/vis_type_vislib/server/index.ts +++ b/src/plugins/vis_type_vislib/server/index.ts @@ -16,14 +16,10 @@ * specific language governing permissions and limitations * under the License. */ - import { schema } from '@kbn/config-schema'; - +import { VisTypeVislibServerPlugin } from './plugin'; export const config = { schema: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), }; -export const plugin = () => ({ - setup() {}, - start() {}, -}); +export const plugin = () => new VisTypeVislibServerPlugin(); diff --git a/packages/kbn-plugin-helpers/lib/index.js b/src/plugins/vis_type_vislib/server/plugin.ts similarity index 69% rename from packages/kbn-plugin-helpers/lib/index.js rename to src/plugins/vis_type_vislib/server/plugin.ts index 836d9de7849dc4..7ff81e070f7a5b 100644 --- a/packages/kbn-plugin-helpers/lib/index.js +++ b/src/plugins/vis_type_vislib/server/plugin.ts @@ -17,16 +17,18 @@ * under the License. */ -const run = require('./run'); -const utils = require('./utils'); +import { CoreSetup, CoreStart, Plugin } from 'kibana/server'; +import { uiSettings } from './ui_settings'; -module.exports = function() { - console.error( - 'running tasks with the default export of @kbn/plugin-helpers is deprecated.' + - "use `require('@kbn/plugin-helpers').run()` instead" - ); +export class VisTypeVislibServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.uiSettings.register(uiSettings); + return {}; + } - return run.apply(this, arguments); -}; + public start(core: CoreStart) { + return {}; + } -Object.assign(module.exports, { run: run }, utils); + public stop() {} +} diff --git a/src/plugins/vis_type_vislib/server/ui_settings.ts b/src/plugins/vis_type_vislib/server/ui_settings.ts new file mode 100644 index 00000000000000..a48cbbae3d0cae --- /dev/null +++ b/src/plugins/vis_type_vislib/server/ui_settings.ts @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { UiSettingsParams } from 'kibana/server'; +import { DIMMING_OPACITY_SETTING, HEATMAP_MAX_BUCKETS_SETTING } from '../common'; + +export const uiSettings: Record = { + [DIMMING_OPACITY_SETTING]: { + name: i18n.translate('visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle', { + defaultMessage: 'Dimming opacity', + }), + value: 0.5, + type: 'number', + description: i18n.translate('visTypeVislib.advancedSettings.visualization.dimmingOpacityText', { + defaultMessage: + 'The opacity of the chart items that are dimmed when highlighting another element of the chart. ' + + 'The lower this number, the more the highlighted element will stand out. ' + + 'This must be a number between 0 and 1.', + }), + category: ['visualization'], + schema: schema.number(), + }, + [HEATMAP_MAX_BUCKETS_SETTING]: { + name: i18n.translate('visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle', { + defaultMessage: 'Heatmap maximum buckets', + }), + value: 50, + type: 'number', + description: i18n.translate( + 'visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText', + { + defaultMessage: + 'The maximum number of buckets a single datasource can return. ' + + 'A higher number might have negative impact on browser rendering performance', + } + ), + category: ['visualization'], + schema: schema.number(), + }, +}; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 9f0cfd7bf4d58a..0306b943cbf2bd 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -264,8 +264,7 @@ export class VisualizeEmbeddable extends Embeddable
diff --git a/src/plugins/visualize/public/application/listing/visualize_listing.js b/src/plugins/visualize/public/application/listing/visualize_listing.js index 2a8c80f9352a5c..7c95fd6a7f4b03 100644 --- a/src/plugins/visualize/public/application/listing/visualize_listing.js +++ b/src/plugins/visualize/public/application/listing/visualize_listing.js @@ -44,6 +44,7 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor toastNotifications, visualizations, core: { docLinks, savedObjects, uiSettings, application }, + savedObjects: savedObjectsPublic, } = getServices(); chrome.docTitle.change( @@ -121,7 +122,7 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor this.fetchItems = filter => { const isLabsEnabled = uiSettings.get('visualize:enableLabs'); return savedVisualizations - .findListItems(filter, uiSettings.get('savedObjects:listingLimit')) + .findListItems(filter, savedObjectsPublic.settings.getListingLimit()) .then(result => { this.totalItems = result.total; @@ -154,7 +155,8 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor }, ]); - this.listingLimit = uiSettings.get('savedObjects:listingLimit'); + this.listingLimit = savedObjectsPublic.settings.getListingLimit(); + this.initialPageSize = savedObjectsPublic.settings.getPerPage(); addHelpMenuToAppChrome(chrome, docLinks); diff --git a/src/plugins/visualize/public/application/listing/visualize_listing_table.js b/src/plugins/visualize/public/application/listing/visualize_listing_table.js index 3d49194c6778fc..a1cc11c93cafc8 100644 --- a/src/plugins/visualize/public/application/listing/visualize_listing_table.js +++ b/src/plugins/visualize/public/application/listing/visualize_listing_table.js @@ -45,6 +45,7 @@ class VisualizeListingTable extends Component { editItem={visualizeCapabilities.save ? this.props.editItem : null} tableColumns={this.getTableColumns()} listingLimit={this.props.listingLimit} + initialPageSize={this.props.initialPageSize} selectable={item => item.canDelete} initialFilter={''} noItemsFragment={this.getNoItemsMessage()} diff --git a/src/plugins/visualize/public/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts index 7642c6575f2118..ace9e50d400c54 100644 --- a/src/plugins/visualize/public/kibana_services.ts +++ b/src/plugins/visualize/public/kibana_services.ts @@ -36,6 +36,7 @@ import { SavedVisualizations } from './application/types'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { DefaultEditorController } from '../../vis_default_editor/public'; import { DashboardStart } from '../../dashboard/public'; +import { SavedObjectsStart } from '../../saved_objects/public'; export interface VisualizeKibanaServices { pluginInitializerContext: PluginInitializerContext; @@ -58,6 +59,7 @@ export interface VisualizeKibanaServices { DefaultVisualizationEditor: typeof DefaultEditorController; createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject']; scopedHistory: () => ScopedHistory; + savedObjects: SavedObjectsStart; } let services: VisualizeKibanaServices | null = null; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 45316fd6fad626..9d1e89a024b611 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -43,6 +43,7 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/publ import { DefaultEditorController } from '../../vis_default_editor/public'; import { DashboardStart } from '../../dashboard/public'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; +import { SavedObjectsStart } from '../../saved_objects/public'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -51,6 +52,7 @@ export interface VisualizePluginStartDependencies { visualizations: VisualizationsStart; dashboard: DashboardStart; kibanaLegacy: KibanaLegacyStart; + savedObjects: SavedObjectsStart; } export interface VisualizePluginSetupDependencies { @@ -135,6 +137,7 @@ export class VisualizePlugin pluginsStart.visualizations.__LEGACY.createVisEmbeddableFromObject, dashboard: pluginsStart.dashboard, scopedHistory: () => this.currentHistory!, + savedObjects: pluginsStart.savedObjects, }; setServices(deps); diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts index 7f01c64d291a53..e72ded3a6f2fd7 100644 --- a/test/common/services/security/test_user.ts +++ b/test/common/services/security/test_user.ts @@ -19,8 +19,8 @@ import { Role } from './role'; import { User } from './user'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { Browser } from '../../../functional/services/browser'; -import { TestSubjects } from '../../../functional/services/test_subjects'; +import { Browser } from '../../../functional/services/common'; +import { TestSubjects } from '../../../functional/services/common'; export async function createTestUserService( role: Role, diff --git a/test/functional/apps/home/_newsfeed.ts b/test/functional/apps/home/_newsfeed.ts index c5891c704dd6a1..096e237850c72d 100644 --- a/test/functional/apps/home/_newsfeed.ts +++ b/test/functional/apps/home/_newsfeed.ts @@ -60,8 +60,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { } }); - // TODO currently fails because styles are not correctly applied in the new platform - it.skip('clicking on newsfeed icon should close opened newsfeed', async () => { + it('clicking on newsfeed icon should close opened newsfeed', async () => { await globalNav.clickNewsfeed(); const isOpen = await PageObjects.newsfeed.openNewsfeedPanel(); expect(isOpen).to.be(false); diff --git a/test/functional/apps/saved_objects_management/edit_saved_object.ts b/test/functional/apps/saved_objects_management/edit_saved_object.ts index 6af91ac9c5c949..1a85ff86498dc7 100644 --- a/test/functional/apps/saved_objects_management/edit_saved_object.ts +++ b/test/functional/apps/saved_objects_management/edit_saved_object.ts @@ -26,6 +26,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'settings']); + const browser = getService('browser'); + const find = getService('find'); const setFieldValue = async (fieldName: string, value: string) => { return testSubjects.setValue(`savedObjects-editField-${fieldName}`, value); @@ -35,6 +37,26 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { return testSubjects.getAttribute(`savedObjects-editField-${fieldName}`, 'value'); }; + const setAceEditorFieldValue = async (fieldName: string, fieldValue: string) => { + const editorId = `savedObjects-editField-${fieldName}-aceEditor`; + await find.clickByCssSelector(`#${editorId}`); + return browser.execute( + (editor: string, value: string) => { + return (window as any).ace.edit(editor).setValue(value); + }, + editorId, + fieldValue + ); + }; + + const getAceEditorFieldValue = async (fieldName: string) => { + const editorId = `savedObjects-editField-${fieldName}-aceEditor`; + await find.clickByCssSelector(`#${editorId}`); + return browser.execute((editor: string) => { + return (window as any).ace.edit(editor).getValue() as string; + }, editorId); + }; + const focusAndClickButton = async (buttonSubject: string) => { const button = await testSubjects.find(buttonSubject); await button.scrollIntoViewIfNecessary(); @@ -99,5 +121,52 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const objects = await PageObjects.settings.getSavedObjectsInTable(); expect(objects.includes('A Dashboard')).to.be(false); }); + + it('preserves the object references when saving', async () => { + const testVisualizationUrl = + '/management/kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed'; + const visualizationRefs = [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: 'logstash-*', + }, + ]; + + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + + const objects = await PageObjects.settings.getSavedObjectsInTable(); + expect(objects.includes('A Pie')).to.be(true); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + await testSubjects.existOrFail('savedObjectEditSave'); + + let displayedReferencesValue = await getAceEditorFieldValue('references'); + + expect(JSON.parse(displayedReferencesValue)).to.eql(visualizationRefs); + + await focusAndClickButton('savedObjectEditSave'); + + await PageObjects.settings.getSavedObjectsInTable(); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + // Parsing to avoid random keys ordering issues in raw string comparison + expect(JSON.parse(await getAceEditorFieldValue('references'))).to.eql(visualizationRefs); + + await setAceEditorFieldValue('references', JSON.stringify([], undefined, 2)); + + await focusAndClickButton('savedObjectEditSave'); + + await PageObjects.settings.getSavedObjectsInTable(); + + await PageObjects.common.navigateToActualUrl('kibana', testVisualizationUrl); + + displayedReferencesValue = await getAceEditorFieldValue('references'); + + expect(JSON.parse(displayedReferencesValue)).to.eql([]); + }); }); } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json index f085bad4c507ec..cbaa0306f9a42f 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/data.json @@ -38,7 +38,14 @@ }, "type": "visualization", "updated_at": "2019-01-22T19:32:31.206Z" - } + }, + "references" : [ + { + "name" : "kibanaSavedObjectMeta.searchSourceJSON.index", + "type" : "index-pattern", + "id" : "logstash-*" + } + ] } } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 0671f26ecd149a..2d0dcb9d1f5d66 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -34,7 +34,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo const find = getService('find'); const globalNav = getService('globalNav'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['shield']); + const PageObjects = getPageObjects(['login']); const defaultTryTimeout = config.get('timeouts.try'); const defaultFindTimeout = config.get('timeouts.find'); @@ -83,12 +83,12 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo if (loginPage && !wantedLoginPage) { log.debug('Found login page'); if (config.get('security.disableTestUser')) { - await PageObjects.shield.login( + await PageObjects.login.login( config.get('servers.kibana.username'), config.get('servers.kibana.password') ); } else { - await PageObjects.shield.login('test_user', 'changeme'); + await PageObjects.login.login('test_user', 'changeme'); } await find.byCssSelector( diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 01301109b80efb..10b09c742f58e7 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -28,7 +28,7 @@ import { HomePageProvider } from './home_page'; import { NewsfeedPageProvider } from './newsfeed_page'; import { SettingsPageProvider } from './settings_page'; import { SharePageProvider } from './share_page'; -import { ShieldPageProvider } from './shield_page'; +import { LoginPageProvider } from './login_page'; import { TimePickerProvider } from './time_picker'; import { TimelionPageProvider } from './timelion_page'; import { VisualBuilderPageProvider } from './visual_builder_page'; @@ -51,7 +51,7 @@ export const pageObjects = { newsfeed: NewsfeedPageProvider, settings: SettingsPageProvider, share: SharePageProvider, - shield: ShieldPageProvider, + login: LoginPageProvider, timelion: TimelionPageProvider, timePicker: TimePickerProvider, visualBuilder: VisualBuilderPageProvider, diff --git a/test/functional/page_objects/shield_page.ts b/test/functional/page_objects/login_page.ts similarity index 90% rename from test/functional/page_objects/shield_page.ts rename to test/functional/page_objects/login_page.ts index 2b9c59373a8bc3..c84f47a342155a 100644 --- a/test/functional/page_objects/shield_page.ts +++ b/test/functional/page_objects/login_page.ts @@ -19,10 +19,10 @@ import { FtrProviderContext } from '../ftr_provider_context'; -export function ShieldPageProvider({ getService }: FtrProviderContext) { +export function LoginPageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - class ShieldPage { + class LoginPage { async login(user: string, pwd: string) { await testSubjects.setValue('loginUsername', user); await testSubjects.setValue('loginPassword', pwd); @@ -30,5 +30,5 @@ export function ShieldPageProvider({ getService }: FtrProviderContext) { } } - return new ShieldPage(); + return new LoginPage(); } diff --git a/test/functional/services/browser.ts b/test/functional/services/common/browser.ts similarity index 98% rename from test/functional/services/browser.ts rename to test/functional/services/common/browser.ts index 13d2365c07191b..4b5a3c101b51ce 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/common/browser.ts @@ -24,10 +24,10 @@ import { LegacyActionSequence } from 'selenium-webdriver/lib/actions'; import { ProvidedType } from '@kbn/test/types/ftr'; import Jimp from 'jimp'; -import { modifyUrl } from '../../../src/core/utils'; -import { WebElementWrapper } from './lib/web_element_wrapper'; -import { FtrProviderContext } from '../ftr_provider_context'; -import { Browsers } from './remote/browsers'; +import { modifyUrl } from '../../../../src/core/utils'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { Browsers } from '../remote/browsers'; export type Browser = ProvidedType; export async function BrowserProvider({ getService }: FtrProviderContext) { diff --git a/test/functional/services/failure_debugging.ts b/test/functional/services/common/failure_debugging.ts similarity index 97% rename from test/functional/services/failure_debugging.ts rename to test/functional/services/common/failure_debugging.ts index cd12f1b75c8281..aa67c455e0100a 100644 --- a/test/functional/services/failure_debugging.ts +++ b/test/functional/services/common/failure_debugging.ts @@ -22,7 +22,7 @@ import { writeFile, mkdir } from 'fs'; import { promisify } from 'util'; import del from 'del'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; interface Test { fullTitle(): string; diff --git a/test/functional/services/find.ts b/test/functional/services/common/find.ts similarity index 99% rename from test/functional/services/find.ts rename to test/functional/services/common/find.ts index 3697e944610743..727d81377b1416 100644 --- a/test/functional/services/find.ts +++ b/test/functional/services/common/find.ts @@ -18,8 +18,8 @@ */ import { WebDriver, WebElement, By, until } from 'selenium-webdriver'; -import { FtrProviderContext } from '../ftr_provider_context'; -import { WebElementWrapper } from './lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; export async function FindProvider({ getService }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/services/common/index.ts b/test/functional/services/common/index.ts new file mode 100644 index 00000000000000..fc483ca512679a --- /dev/null +++ b/test/functional/services/common/index.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { BrowserProvider, Browser } from './browser'; +export { FailureDebuggingProvider } from './failure_debugging'; +export { FindProvider } from './find'; +export { ScreenshotsProvider } from './screenshots'; +export { SnapshotsProvider } from './snapshots'; +export { TestSubjectsProvider, TestSubjects } from './test_subjects'; diff --git a/test/functional/services/screenshots.ts b/test/functional/services/common/screenshots.ts similarity index 95% rename from test/functional/services/screenshots.ts rename to test/functional/services/common/screenshots.ts index 4c5728174cf992..daa55240f3eb77 100644 --- a/test/functional/services/screenshots.ts +++ b/test/functional/services/common/screenshots.ts @@ -23,9 +23,9 @@ import { promisify } from 'util'; import del from 'del'; -import { comparePngs } from './lib/compare_pngs'; -import { FtrProviderContext } from '../ftr_provider_context'; -import { WebElementWrapper } from './lib/web_element_wrapper'; +import { comparePngs } from '../lib/compare_pngs'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; const mkdirAsync = promisify(mkdir); const writeFileAsync = promisify(writeFile); diff --git a/test/functional/services/snapshots.ts b/test/functional/services/common/snapshots.ts similarity index 97% rename from test/functional/services/snapshots.ts rename to test/functional/services/common/snapshots.ts index 84526878a7bb45..2e0b360e594e53 100644 --- a/test/functional/services/snapshots.ts +++ b/test/functional/services/common/snapshots.ts @@ -23,7 +23,7 @@ import { promisify } from 'util'; import expect from '@kbn/expect'; import del from 'del'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; const mkdirAsync = promisify(mkdir); const writeFileAsync = promisify(writeFile); diff --git a/test/functional/services/test_subjects.ts b/test/functional/services/common/test_subjects.ts similarity index 99% rename from test/functional/services/test_subjects.ts rename to test/functional/services/common/test_subjects.ts index 090dc995ddc112..e4e0e7ce70bc40 100644 --- a/test/functional/services/test_subjects.ts +++ b/test/functional/services/common/test_subjects.ts @@ -20,8 +20,8 @@ import testSubjSelector from '@kbn/test-subj-selector'; import { map as mapAsync } from 'bluebird'; import { ProvidedType } from '@kbn/test/types/ftr'; -import { WebElementWrapper } from './lib/web_element_wrapper'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; interface ExistsOptions { timeout?: number; diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 02ed9e9865d9a3..cbb0c6790dbe94 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -20,7 +20,14 @@ import { services as commonServiceProviders } from '../../common/services'; import { AppsMenuProvider } from './apps_menu'; -import { BrowserProvider } from './browser'; +import { + BrowserProvider, + FailureDebuggingProvider, + FindProvider, + ScreenshotsProvider, + SnapshotsProvider, + TestSubjectsProvider, +} from './common'; import { ComboBoxProvider } from './combo_box'; import { DashboardAddPanelProvider, @@ -33,19 +40,14 @@ import { import { DocTableProvider } from './doc_table'; import { ElasticChartProvider } from './elastic_chart'; import { EmbeddingProvider } from './embedding'; -import { FailureDebuggingProvider } from './failure_debugging'; import { FilterBarProvider } from './filter_bar'; -import { FindProvider } from './find'; import { FlyoutProvider } from './flyout'; import { GlobalNavProvider } from './global_nav'; import { InspectorProvider } from './inspector'; import { QueryBarProvider } from './query_bar'; import { RemoteProvider } from './remote'; import { RenderableProvider } from './renderable'; -import { ScreenshotsProvider } from './screenshots'; -import { SnapshotsProvider } from './snapshots'; import { TableProvider } from './table'; -import { TestSubjectsProvider } from './test_subjects'; import { ToastsProvider } from './toasts'; // @ts-ignore not TS yet import { PieChartProvider } from './visualizations'; diff --git a/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx b/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx index f3b7a19f70ae3b..96297f6d51566c 100644 --- a/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx +++ b/test/plugin_functional/plugins/management_test_plugin/public/plugin.tsx @@ -21,22 +21,17 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Switch, Route, Link } from 'react-router-dom'; import { CoreSetup, Plugin } from 'kibana/public'; -import { ManagementSetup } from '../../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../../src/plugins/management/public'; export class ManagementTestPlugin implements Plugin { public setup(core: CoreSetup, { management }: { management: ManagementSetup }) { - const testSection = management.sections.register({ - id: 'test-section', - title: 'Test Section', - euiIconType: 'logoKibana', - order: 25, - }); + const testSection = management.sections.getSection(ManagementSectionId.Data); - testSection!.registerApp({ + testSection.registerApp({ id: 'test-management', title: 'Management Test', - mount(params) { + mount(params: any) { params.setBreadcrumbs([{ text: 'Management Test' }]); ReactDOM.render( @@ -63,7 +58,7 @@ export class ManagementTestPlugin }, }); - testSection! + testSection .registerApp({ id: 'test-management-disabled', title: 'Management Test Disabled', diff --git a/test/plugin_functional/test_suites/management/management_plugin.js b/test/plugin_functional/test_suites/management/management_plugin.js index 0c185f4b385b51..6ad2bb56391ddd 100644 --- a/test/plugin_functional/test_suites/management/management_plugin.js +++ b/test/plugin_functional/test_suites/management/management_plugin.js @@ -40,7 +40,7 @@ export default function({ getService, getPageObjects }) { it('should redirect when app is disabled', async () => { await PageObjects.common.navigateToActualUrl( 'kibana', - 'management/test-section/test-management-disabled' + 'management/data/test-management-disabled' ); await testSubjects.existOrFail('management-landing'); }); diff --git a/test/ui_capabilities/newsfeed_err/test.ts b/test/ui_capabilities/newsfeed_err/test.ts index 07c829e8b15a08..2aa81f34028a02 100644 --- a/test/ui_capabilities/newsfeed_err/test.ts +++ b/test/ui_capabilities/newsfeed_err/test.ts @@ -51,8 +51,7 @@ export default function uiCapabilitiesTests({ getService, getPageObjects }: FtrP expect(objects).to.eql([]); }); - // TODO currently fails because styles are not correctly applied in the new platform - it.skip('clicking on newsfeed icon should close opened newsfeed', async () => { + it('clicking on newsfeed icon should close opened newsfeed', async () => { await globalNav.clickNewsfeed(); const isOpen = await PageObjects.newsfeed.openNewsfeedPanel(); expect(isOpen).to.be(false); diff --git a/vars/agentInfo.groovy b/vars/agentInfo.groovy index cb20b434ba34cf..166a86c1692616 100644 --- a/vars/agentInfo.groovy +++ b/vars/agentInfo.groovy @@ -5,7 +5,7 @@ def print() { def resourcesUrl = ( - "https://infra-stats.elastic.co/app/visualize#/edit/8bd92360-1b92-11ea-b719-aba04518cc34" + + "https://infra-stats.elastic.co/app/kibana#/visualize/edit/8bd92360-1b92-11ea-b719-aba04518cc34" + "?_g=(time:(from:'${startTime}',to:'${endTime}'))" + "&_a=(query:'host.name:${env.NODE_NAME}')" ) diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 6252a103d28813..f4c44a8ae2df4e 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -178,7 +178,18 @@ def bash(script, label) { } def doSetup() { - runbld("./test/scripts/jenkins_setup.sh", "Setup Build Environment and Dependencies") + retryWithDelay(2, 15) { + try { + runbld("./test/scripts/jenkins_setup.sh", "Setup Build Environment and Dependencies") + } catch (ex) { + try { + // Setup expects this directory to be missing, so we need to remove it before we do a retry + bash("rm -rf ../elasticsearch", "Remove elasticsearch sibling directory, if it exists") + } finally { + throw ex + } + } + } } def buildOss() { diff --git a/vars/slackNotifications.groovy b/vars/slackNotifications.groovy index 8ae37d1c44637c..5092f631e7b397 100644 --- a/vars/slackNotifications.groovy +++ b/vars/slackNotifications.groovy @@ -62,21 +62,32 @@ def getTestFailures() { def messages = [] messages << "*Test Failures*" - def list = failures.collect { "• <${it.url}|${it.fullDisplayName}>" }.join("\n") + def list = failures.collect { "• <${it.url}|${it.fullDisplayName.split('.', 2)[-1]}>" }.join("\n") return "*Test Failures*\n${list}" } -def sendFailedBuild(Map params = [:]) { - def displayName = "${env.JOB_NAME} ${env.BUILD_DISPLAY_NAME}" +def getDefaultDisplayName() { + return "${env.JOB_NAME} ${env.BUILD_DISPLAY_NAME}" +} +def getDefaultContext() { + def duration = currentBuild.durationString.replace(' and counting', '') + + return contextBlock([ + "${buildUtils.getBuildStatus().toLowerCase().capitalize()} after ${duration}", + "", + ].join(' · ')) +} + +def sendFailedBuild(Map params = [:]) { def config = [ - channel: '#kibana-operations', - title: ":broken_heart: *<${env.BUILD_URL}|${displayName}>*", - message: ":broken_heart: ${displayName}", + channel: '#kibana-operations-alerts', + title: ":broken_heart: *<${env.BUILD_URL}|${getDefaultDisplayName()}>*", + message: ":broken_heart: ${getDefaultDisplayName()}", color: 'danger', icon: ':jenkins:', username: 'Kibana Operations', - context: contextBlock("${displayName} · "), + context: getDefaultContext(), ] + params def blocks = [markdownBlock(config.title)] @@ -94,18 +105,24 @@ def sendFailedBuild(Map params = [:]) { ) } +def onFailure(Map options = [:]) { + catchError { + def status = buildUtils.getBuildStatus() + if (status != "SUCCESS") { + catchErrors { + sendFailedBuild(options) + } + } + } +} + def onFailure(Map options = [:], Closure closure) { // try/finally will NOT work here, because the build status will not have been changed to ERROR when the finally{} block executes catchError { closure() } - def status = buildUtils.getBuildStatus() - if (status != "SUCCESS" && status != "UNSTABLE") { - catchErrors { - sendFailedBuild(options) - } - } + onFailure(options) } return this diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 7c464d44d57610..7ac27dd47ad64e 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -7,7 +7,7 @@ "xpack.alerting": "plugins/alerting", "xpack.alertingBuiltins": "plugins/alerting_builtins", "xpack.apm": ["legacy/plugins/apm", "plugins/apm"], - "xpack.beatsManagement": "legacy/plugins/beats_management", + "xpack.beatsManagement": ["legacy/plugins/beats_management", "plugins/beats_management"], "xpack.canvas": "plugins/canvas", "xpack.dashboard": "plugins/dashboard_enhanced", "xpack.crossClusterReplication": "plugins/cross_cluster_replication", diff --git a/x-pack/legacy/plugins/beats_management/common/constants/index.ts b/x-pack/legacy/plugins/beats_management/common/constants/index.ts index 31fa84906cd38a..8d22b36e96d45f 100644 --- a/x-pack/legacy/plugins/beats_management/common/constants/index.ts +++ b/x-pack/legacy/plugins/beats_management/common/constants/index.ts @@ -9,4 +9,4 @@ export { INDEX_NAMES } from './index_names'; export { PLUGIN } from './plugin'; export { LICENSES, REQUIRED_LICENSES, REQUIRED_ROLES } from './security'; export { TABLE_CONFIG } from './table'; -export const BASE_PATH = '/management/beats_management'; +export const BASE_PATH = '/management/ingest/beats_management'; diff --git a/x-pack/legacy/plugins/beats_management/index.ts b/x-pack/legacy/plugins/beats_management/index.ts index 3355bc98818118..b5399595fb7828 100644 --- a/x-pack/legacy/plugins/beats_management/index.ts +++ b/x-pack/legacy/plugins/beats_management/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import Joi from 'joi'; -import { resolve } from 'path'; import { PLUGIN } from './common/constants'; import { CONFIG_PREFIX } from './common/constants/plugin'; import { initServerWithKibana } from './server/kibana.index'; @@ -29,10 +28,6 @@ export function beats(kibana: any) { return new kibana.Plugin({ id: PLUGIN.ID, require: ['kibana', 'elasticsearch', 'xpack_main'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - managementSections: ['plugins/beats_management'], - }, config: () => config, configPrefix: CONFIG_PREFIX, init(server: KibanaLegacyServer) { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts deleted file mode 100644 index b2cfd826e62079..00000000000000 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/* eslint-disable max-classes-per-file */ -import { IScope } from 'angular'; -import { PathReporter } from 'io-ts/lib/PathReporter'; -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { UIRoutes } from 'ui/routes'; -import { isLeft } from 'fp-ts/lib/Either'; -import { npSetup } from 'ui/new_platform'; -import { SecurityPluginSetup } from '../../../../../../../plugins/security/public'; -import { BufferedKibanaServiceCall, KibanaAdapterServiceRefs, KibanaUIConfig } from '../../types'; -import { - FrameworkAdapter, - FrameworkInfo, - FrameworkUser, - ManagementAPI, - RuntimeFrameworkInfo, - RuntimeFrameworkUser, -} from './adapter_types'; -interface IInjector { - get(injectable: string): any; -} - -export class KibanaFrameworkAdapter implements FrameworkAdapter { - public get info() { - if (this.xpackInfo) { - return this.xpackInfo; - } else { - throw new Error('framework adapter must have init called before anything else'); - } - } - - public get currentUser() { - return this.shieldUser!; - } - private xpackInfo: FrameworkInfo | null = null; - private adapterService: KibanaAdapterServiceProvider; - private shieldUser: FrameworkUser | null = null; - constructor( - private readonly PLUGIN_ID: string, - private readonly management: ManagementAPI, - private readonly routes: UIRoutes, - private readonly getBasePath: () => string, - private readonly onKibanaReady: () => Promise, - private readonly xpackInfoService: any | null, - public readonly version: string - ) { - this.adapterService = new KibanaAdapterServiceProvider(); - } - - public setUISettings = (key: string, value: any) => { - this.adapterService.callOrBuffer(({ config }) => { - config.set(key, value); - }); - }; - - public async waitUntilFrameworkReady(): Promise { - await this.onKibanaReady(); - const xpackInfo: any = this.xpackInfoService; - let xpackInfoUnpacked: FrameworkInfo; - - try { - xpackInfoUnpacked = { - basePath: this.getBasePath(), - license: { - type: xpackInfo ? xpackInfo.getLicense().type : 'oss', - expired: xpackInfo ? !xpackInfo.getLicense().isActive : false, - expiry_date_in_millis: - xpackInfo.getLicense().expiryDateInMillis !== undefined - ? xpackInfo.getLicense().expiryDateInMillis - : -1, - }, - security: { - enabled: xpackInfo - ? xpackInfo.get(`features.${this.PLUGIN_ID}.security.enabled`, false) - : false, - available: xpackInfo - ? xpackInfo.get(`features.${this.PLUGIN_ID}.security.available`, false) - : false, - }, - settings: xpackInfo ? xpackInfo.get(`features.${this.PLUGIN_ID}.settings`) : {}, - }; - } catch (e) { - throw new Error(`Unexpected data structure from xpackInfoService, ${JSON.stringify(e)}`); - } - - const assertData = RuntimeFrameworkInfo.decode(xpackInfoUnpacked); - if (isLeft(assertData)) { - throw new Error( - `Error parsing xpack info in ${this.PLUGIN_ID}, ${PathReporter.report(assertData)[0]}` - ); - } - this.xpackInfo = xpackInfoUnpacked; - - const securitySetup = ((npSetup.plugins as unknown) as { security?: SecurityPluginSetup }) - .security; - try { - this.shieldUser = (await securitySetup?.authc.getCurrentUser()) || null; - const assertUser = RuntimeFrameworkUser.decode(this.shieldUser); - - if (isLeft(assertUser)) { - throw new Error( - `Error parsing user info in ${this.PLUGIN_ID}, ${PathReporter.report(assertUser)[0]}` - ); - } - } catch (e) { - this.shieldUser = null; - } - } - - public renderUIAtPath( - path: string, - component: React.ReactElement, - toController: 'management' | 'self' = 'self' - ) { - const adapter = this; - this.routes.when( - `${path}${[...Array(6)].map((e, n) => `/:arg${n}?`).join('')}`, // Hack because angular 1 does not support wildcards - { - template: - toController === 'self' - ? `<${this.PLUGIN_ID}>
` - : ` -
-
- `, - // eslint-disable-next-line max-classes-per-file - controller: ($scope: any, $route: any) => { - try { - $scope.$$postDigest(() => { - const elem = document.getElementById(`${this.PLUGIN_ID}ReactRoot`); - ReactDOM.render(component, elem); - adapter.manageAngularLifecycle($scope, $route, elem); - }); - $scope.$onInit = () => { - $scope.topNavMenu = []; - }; - } catch (e) { - throw new Error(`Error rendering Beats CM to the dom, ${e.message}`); - } - }, - } - ); - } - - public registerManagementSection(settings: { - id?: string; - name: string; - iconName: string; - order?: number; - }) { - const sectionId = settings.id || this.PLUGIN_ID; - - if (!this.management.hasItem(sectionId)) { - this.management.register(sectionId, { - display: settings.name, - icon: settings.iconName, - order: settings.order || 30, - }); - } - } - - public registerManagementUI(settings: { - sectionId?: string; - name: string; - basePath: string; - visable?: boolean; - order?: number; - }) { - const sectionId = settings.sectionId || this.PLUGIN_ID; - - if (!this.management.hasItem(sectionId)) { - throw new Error( - `registerManagementUI was called with a sectionId of ${sectionId}, and that is is not yet regestered as a section` - ); - } - - const section = this.management.getSection(sectionId); - - section.register(sectionId, { - visible: settings.visable || true, - display: settings.name, - order: settings.order || 30, - url: `#${settings.basePath}`, - }); - } - - private manageAngularLifecycle($scope: any, $route: any, elem: any) { - const lastRoute = $route.current; - const deregister = $scope.$on('$locationChangeSuccess', () => { - const currentRoute = $route.current; - // if templates are the same we are on the same route - if (lastRoute.$$route.template === currentRoute.$$route.template) { - // this prevents angular from destroying scope - $route.current = lastRoute; - } else { - if (elem) { - ReactDOM.unmountComponentAtNode(elem); - elem.remove(); - } - } - }); - $scope.$on('$destroy', () => { - if (deregister) { - deregister(); - } - - // manually unmount component when scope is destroyed - if (elem) { - ReactDOM.unmountComponentAtNode(elem); - elem.remove(); - } - }); - } -} - -class KibanaAdapterServiceProvider { - public serviceRefs: KibanaAdapterServiceRefs | null = null; - public bufferedCalls: Array> = []; - - public $get($rootScope: IScope, config: KibanaUIConfig) { - this.serviceRefs = { - config, - rootScope: $rootScope, - }; - - this.applyBufferedCalls(this.bufferedCalls); - - return this; - } - - public callOrBuffer(serviceCall: (serviceRefs: KibanaAdapterServiceRefs) => void) { - if (this.serviceRefs !== null) { - this.applyBufferedCalls([serviceCall]); - } else { - this.bufferedCalls.push(serviceCall); - } - } - - public applyBufferedCalls( - bufferedCalls: Array> - ) { - if (!this.serviceRefs) { - return; - } - - this.serviceRefs.rootScope.$apply(() => { - bufferedCalls.forEach(serviceCall => { - if (!this.serviceRefs) { - return; - } - return serviceCall(this.serviceRefs); - }); - }); - } -} diff --git a/x-pack/legacy/plugins/beats_management/public/lib/compose/memory.ts b/x-pack/legacy/plugins/beats_management/public/lib/compose/memory.ts deleted file mode 100644 index b8ecb644ff1b07..00000000000000 --- a/x-pack/legacy/plugins/beats_management/public/lib/compose/memory.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import 'ui/autoload/all'; -// @ts-ignore: path dynamic for kibana -import { management } from 'ui/management'; -// @ts-ignore: path dynamic for kibana -import { uiModules } from 'ui/modules'; -// @ts-ignore: path dynamic for kibana -import routes from 'ui/routes'; -import { configBlockSchemas } from '../../../common/config_schemas'; -import { translateConfigSchema } from '../../../common/config_schemas_translations_map'; -// @ts-ignore: path dynamic for kibana -import { MemoryBeatsAdapter } from '../adapters/beats/memory_beats_adapter'; -import { KibanaFrameworkAdapter } from '../adapters/framework/kibana_framework_adapter'; -import { MemoryTagsAdapter } from '../adapters/tags/memory_tags_adapter'; -import { MemoryTokensAdapter } from '../adapters/tokens/memory_tokens_adapter'; -import { BeatsLib } from '../beats'; -import { ConfigBlocksLib } from '../configuration_blocks'; -import { FrameworkLib } from '../framework'; -import { TagsLib } from '../tags'; -import { FrontendLibs } from '../types'; -import { MemoryElasticsearchAdapter } from './../adapters/elasticsearch/memory'; -import { ElasticsearchLib } from './../elasticsearch'; -import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; - -const onKibanaReady = uiModules.get('kibana').run; - -export function compose( - mockIsKueryValid: (kuery: string) => boolean, - mockKueryToEsQuery: (kuery: string) => string, - suggestions: QuerySuggestion[] -): FrontendLibs { - const esAdapter = new MemoryElasticsearchAdapter( - mockIsKueryValid, - mockKueryToEsQuery, - suggestions - ); - const elasticsearchLib = new ElasticsearchLib(esAdapter); - - const configBlocks = new ConfigBlocksLib({} as any, translateConfigSchema(configBlockSchemas)); - const tags = new TagsLib(new MemoryTagsAdapter([]), elasticsearchLib); - const tokens = new MemoryTokensAdapter(); - const beats = new BeatsLib(new MemoryBeatsAdapter([]), elasticsearchLib); - - const pluginUIModule = uiModules.get('app/beats_management'); - - const framework = new FrameworkLib( - new KibanaFrameworkAdapter( - pluginUIModule, - management, - routes, - () => '', - onKibanaReady, - null, - '7.0.0' - ) - ); - const libs: FrontendLibs = { - framework, - elasticsearch: elasticsearchLib, - tags, - tokens, - beats, - configBlocks, - }; - return libs; -} diff --git a/x-pack/legacy/plugins/beats_management/scripts/fake_env.ts b/x-pack/legacy/plugins/beats_management/scripts/fake_env.ts index e2e31a218943a0..fe8919f48ac106 100644 --- a/x-pack/legacy/plugins/beats_management/scripts/fake_env.ts +++ b/x-pack/legacy/plugins/beats_management/scripts/fake_env.ts @@ -9,7 +9,8 @@ import request from 'request'; import uuidv4 from 'uuid/v4'; import { configBlockSchemas } from '../common/config_schemas'; import { BeatTag } from '../common/domain_types'; -import { compose } from '../public/lib/compose/scripts'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { compose } from '../../../../plugins/beats_management/public/lib/compose/scripts'; const args = process.argv.slice(2); const chance = new Chance(); diff --git a/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/adapter_types.ts b/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/adapter_types.ts index 273584efb239b9..4cb38bb3d057b6 100644 --- a/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/adapter_types.ts +++ b/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/adapter_types.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { BeatEvent } from '../../../../common/domain_types'; -import { FrameworkUser } from '../../../../public/lib/adapters/framework/adapter_types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FrameworkUser } from '../../../../../../../plugins/beats_management/public/lib/adapters/framework/adapter_types'; export interface BeatEventsAdapter { bulkInsert(user: FrameworkUser, beatId: string, events: BeatEvent[]): Promise; diff --git a/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/elasticsearch_beat_events_adapter.ts b/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/elasticsearch_beat_events_adapter.ts index 62759ce0f5215e..b5056140c8b860 100644 --- a/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/elasticsearch_beat_events_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/server/lib/adapters/events/elasticsearch_beat_events_adapter.ts @@ -5,7 +5,8 @@ */ import { BeatEvent } from '../../../../common/domain_types'; -import { FrameworkUser } from '../../../../public/lib/adapters/framework/adapter_types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FrameworkUser } from '../../../../../../../plugins/beats_management/public/lib/adapters/framework/adapter_types'; import { DatabaseAdapter } from '../database/adapter_types'; import { BeatEventsAdapter } from './adapter_types'; diff --git a/x-pack/legacy/plugins/beats_management/server/rest_api/beats/tag_assignment.ts b/x-pack/legacy/plugins/beats_management/server/rest_api/beats/tag_assignment.ts index 90ba4056d7c984..db19e5b6a7627e 100644 --- a/x-pack/legacy/plugins/beats_management/server/rest_api/beats/tag_assignment.ts +++ b/x-pack/legacy/plugins/beats_management/server/rest_api/beats/tag_assignment.ts @@ -7,7 +7,8 @@ import Joi from 'joi'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkAction } from '../../../common/return_types'; -import { BeatsTagAssignment } from '../../../public/lib/adapters/beats/adapter_types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BeatsTagAssignment } from '../../../../../../plugins/beats_management/public/lib/adapters/beats/adapter_types'; import { FrameworkRequest } from '../../lib/adapters/framework/adapter_types'; import { CMServerLibs } from '../../lib/types'; diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index e1532cac858ac0..89ce976de9278d 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -26,9 +26,9 @@ export const plugin = () => { return new MapsPlugin(); }; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { RenderTooltipContentParams, ITooltipProperty, -} from '../../../../plugins/maps/public/classes/tooltips/tooltip_property'; +} from '../../../../plugins/maps/public/classes/tooltips/tooltip_property'; // eslint-disable-line @kbn/eslint/no-restricted-paths +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { MapEmbeddable, MapEmbeddableInput } from '../../../../plugins/maps/public/embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/routes.js b/x-pack/legacy/plugins/maps/public/routes.js index 70c1c4a50efd45..721b30a208ebc7 100644 --- a/x-pack/legacy/plugins/maps/public/routes.js +++ b/x-pack/legacy/plugins/maps/public/routes.js @@ -18,6 +18,7 @@ import { } from '../../../../plugins/maps/public/kibana_services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { getMapsSavedObjectLoader } from '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader'; +import { LISTING_LIMIT_SETTING } from '../../../../../src/plugins/saved_objects/common'; routes.enable(); @@ -43,7 +44,7 @@ routes template: listingTemplate, controller($scope, config) { const gisMapSavedObjectLoader = getMapsSavedObjectLoader(); - $scope.listingLimit = config.get('savedObjects:listingLimit'); + $scope.listingLimit = config.get(LISTING_LIMIT_SETTING); $scope.find = search => { return gisMapSavedObjectLoader.find(search, $scope.listingLimit); }; diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js index e2a758075155a5..b7cd1687dc257c 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js @@ -29,5 +29,9 @@ export function getIndexPatternSettings(indicesSettingsResp) { maxInnerResultWindow = Math.min(indexMaxInnerResultWindow, indexMaxResultWindow); }); - return { maxResultWindow, maxInnerResultWindow }; + return { + maxResultWindow: maxResultWindow === Infinity ? DEFAULT_MAX_RESULT_WINDOW : maxResultWindow, + maxInnerResultWindow: + maxInnerResultWindow === Infinity ? DEFAULT_MAX_INNER_RESULT_WINDOW : maxInnerResultWindow, + }; } diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js index c152f5bfffc31c..46949a2e3e2cf0 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js @@ -24,6 +24,14 @@ describe('max_result_window and max_inner_result_window are not set', () => { expect(maxInnerResultWindow).toBe(DEFAULT_MAX_INNER_RESULT_WINDOW); }); + test('Should provide default values from cross cluster index response', () => { + // _settings returns empty object for cross cluster index + const indicesSettingsResp = {}; + const { maxResultWindow, maxInnerResultWindow } = getIndexPatternSettings(indicesSettingsResp); + expect(maxResultWindow).toBe(DEFAULT_MAX_RESULT_WINDOW); + expect(maxInnerResultWindow).toBe(DEFAULT_MAX_INNER_RESULT_WINDOW); + }); + test('Should include default values when providing minimum values for indices in index pattern', () => { const indicesSettingsResp = { kibana_sample_data_logs: { diff --git a/x-pack/legacy/plugins/xpack_main/index.js b/x-pack/legacy/plugins/xpack_main/index.js index 6ce457ffbec05d..1f8a4a62ea1567 100644 --- a/x-pack/legacy/plugins/xpack_main/index.js +++ b/x-pack/legacy/plugins/xpack_main/index.js @@ -64,10 +64,7 @@ export const xpackMain = kibana => { mirrorPluginStatus(server.plugins.elasticsearch, this, 'yellow', 'red'); - featuresPlugin.registerLegacyAPI({ - xpackInfo: setupXPackMain(server), - savedObjectTypes: server.savedObjects.types, - }); + setupXPackMain(server); // register routes xpackInfoRoute(server); diff --git a/x-pack/legacy/plugins/xpack_main/public/components/index.js b/x-pack/legacy/plugins/xpack_main/public/components/index.js index 871d86e642dec1..0fe056ec0d46fd 100644 --- a/x-pack/legacy/plugins/xpack_main/public/components/index.js +++ b/x-pack/legacy/plugins/xpack_main/public/components/index.js @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { LicenseStatus } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/license_status/license_status'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { AddLicense } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/add_license/add_license'; /* diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx index 75c6c79bc804a0..9001eb6992a96f 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx @@ -82,7 +82,7 @@ export function AlertIntegrations(props: Props) { } ), href: plugin.core.http.basePath.prepend( - '/app/kibana#/management/kibana/triggersActions/alerts' + '/app/kibana#/management/insightsAndAlerting/triggersActions/alerts' ), icon: 'tableOfContents' } diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx index 3bbd8a01d05494..c0c93bb4cb298d 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx @@ -258,7 +258,7 @@ export class WatcherFlyout extends Component< )}{' '} {i18n.translate( 'xpack.apm.serviceDetails.enableErrorReportsPanel.watchCreatedNotificationText.viewWatchLinkText', diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx index 91483b4b52d90e..e1f58b7b352101 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx @@ -92,7 +92,7 @@ export class ServiceIntegrations extends React.Component { ), icon: 'watchesApp', href: core.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/watcher' + '/app/kibana#/management/insightsAndAlerting/watcher' ), target: '_blank', onClick: () => this.closePopover() diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index 5d1ca923cbc8f8..67c6cd9978bf19 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -81,9 +81,6 @@ function getLayoutOptions( fit: true, padding: nodeHeight, spacingFactor: 0.85, - animate: true, - animationEasing: animationOptions.easing, - animationDuration: animationOptions.duration, // @ts-ignore // Rotate nodes counter-clockwise to transform layout from top→bottom to left→right. // The extra 5° achieves the effect of separating overlapping taxi-styled edges. diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index b5bfa63c1bdde0..e58d9a0ff761a7 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -15,7 +15,7 @@ import { import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import cytoscape from 'cytoscape'; -import React from 'react'; +import React, { MouseEvent } from 'react'; import styled from 'styled-components'; import { fontSize, px } from '../../../../style/variables'; import { Buttons } from './Buttons'; @@ -31,7 +31,7 @@ const popoverMinWidth = 280; interface ContentsProps { isService: boolean; label: string; - onFocusClick: () => void; + onFocusClick: (event: MouseEvent) => void; selectedNodeData: cytoscape.NodeDataDefinition; selectedNodeServiceName: string; } diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx index 1c9d5092bfcf5d..86ef0b880c4da6 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx @@ -8,6 +8,7 @@ import { EuiPopover } from '@elastic/eui'; import cytoscape from 'cytoscape'; import React, { CSSProperties, + MouseEvent, useCallback, useContext, useEffect, @@ -16,8 +17,8 @@ import React, { } from 'react'; import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames'; import { CytoscapeContext } from '../Cytoscape'; -import { Contents } from './Contents'; import { animationOptions } from '../cytoscapeOptions'; +import { Contents } from './Contents'; interface PopoverProps { focusedServiceName?: string; @@ -87,14 +88,18 @@ export function Popover({ focusedServiceName }: PopoverProps) { } }, [popoverRef, x, y]); - const centerSelectedNode = useCallback(() => { - if (cy) { - cy.animate({ - ...animationOptions, - center: { eles: cy.getElementById(selectedNodeServiceName) } - }); - } - }, [cy, selectedNodeServiceName]); + const centerSelectedNode = useCallback( + (event: MouseEvent) => { + event.preventDefault(); + if (cy) { + cy.animate({ + ...animationOptions, + center: { eles: cy.getElementById(selectedNodeServiceName) } + }); + } + }, + [cy, selectedNodeServiceName] + ); const isAlreadyFocused = focusedServiceName === selectedNodeServiceName; @@ -110,7 +115,9 @@ export function Popover({ focusedServiceName }: PopoverProps) { deselect() + } selectedNodeData={selectedNodeData} selectedNodeServiceName={selectedNodeServiceName} /> diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index d3c4654de81649..3ec13a4cde20de 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -58,9 +58,9 @@ const getBorderWidth = (el: cytoscape.NodeSingular) => { if (nodeSeverity === severity.minor || nodeSeverity === severity.major) { return 4; } else if (nodeSeverity === severity.critical) { - return 12; + return 8; } else { - return 2; + return 4; } }; diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx index c1afa433cb614b..266e5a97ef07ae 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx @@ -66,7 +66,7 @@ export function NoServicesMessage({ historicalDataFound, status }: Props) { defaultMessage: 'You may also have old data that needs to be migrated.' })}{' '} - + {i18n.translate('xpack.apm.servicesTable.UpgradeAssistantLink', { defaultMessage: 'Learn more by visiting the Kibana Upgrade Assistant' diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap index 227becb9a9c4f8..d027422961c99d 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap @@ -25,7 +25,7 @@ exports[`NoServicesMessage status: pending and historicalDataFound: false 1`] = You may also have old data that needs to be migrated. Learn more by visiting the Kibana Upgrade Assistant @@ -70,7 +70,7 @@ exports[`NoServicesMessage status: success and historicalDataFound: false 1`] = You may also have old data that needs to be migrated. Learn more by visiting the Kibana Upgrade Assistant diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index 6d310199ba9a59..3e6be107ce3a1b 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -79,7 +79,7 @@ NodeList [ Learn more by visiting the Kibana Upgrade Assistant diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx index 99b169e3ec3619..06f56d9ec1be75 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx @@ -66,7 +66,7 @@ export function ServiceOverview() { {i18n.translate( diff --git a/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx b/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx index d2afefb83a568f..96e8c754fcc5f6 100644 --- a/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx @@ -17,7 +17,7 @@ interface Props { export const LicensePrompt = ({ text, showBetaBadge = false }: Props) => { const licensePageUrl = useKibanaUrl( '/app/kibana', - '/management/elasticsearch/license_management/home' + '/management/stack/license_management/home' ); const renderLicenseBody = ( diff --git a/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx b/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx index 36e780f50c3ae3..8ed02f039289ea 100644 --- a/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx +++ b/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx @@ -11,7 +11,7 @@ import { useApmPluginContext } from '../../hooks/useApmPluginContext'; export function InvalidLicenseNotification() { const { core } = useApmPluginContext(); const manageLicenseURL = core.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/license_management' + '/app/kibana#/management/stack/license_management' ); return ( diff --git a/x-pack/plugins/apm/public/setHelpExtension.ts b/x-pack/plugins/apm/public/setHelpExtension.ts index 1a3394651b2ff2..aa23a8a2e64ead 100644 --- a/x-pack/plugins/apm/public/setHelpExtension.ts +++ b/x-pack/plugins/apm/public/setHelpExtension.ts @@ -22,7 +22,7 @@ export function setHelpExtension({ chrome, http }: CoreStart) { linkType: 'custom', href: url.format({ pathname: http.basePath.prepend('/app/kibana'), - hash: '/management/elasticsearch/upgrade_assistant' + hash: '/management/stack/upgrade_assistant' }), content: i18n.translate('xpack.apm.helpMenu.upgradeAssistantLink', { defaultMessage: 'Upgrade assistant' diff --git a/x-pack/plugins/beats_management/common/index.ts b/x-pack/plugins/beats_management/common/index.ts new file mode 100644 index 00000000000000..92ca870810ce8d --- /dev/null +++ b/x-pack/plugins/beats_management/common/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema, TypeOf } from '@kbn/config-schema'; + +const DEFAULT_ENROLLMENT_TOKENS_TTL_S = 10 * 60; // 10 minutes + +export const beatsManagementConfigSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + defaultUserRoles: schema.arrayOf(schema.string(), { defaultValue: ['superuser'] }), + encryptionKey: schema.string({ defaultValue: 'xpack_beats_default_encryptionKey' }), + enrollmentTokensTtlInSeconds: schema.number({ + min: 1, + max: 10 * 60 * 14, // No more then 2 weeks for security reasons, + defaultValue: DEFAULT_ENROLLMENT_TOKENS_TTL_S, + }), +}); + +export type BeatsManagementConfigType = TypeOf; diff --git a/x-pack/plugins/beats_management/kibana.json b/x-pack/plugins/beats_management/kibana.json new file mode 100644 index 00000000000000..1b431216ef9923 --- /dev/null +++ b/x-pack/plugins/beats_management/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "beats_management", + "configPath": ["xpack", "beats_management"], + "ui": true, + "server": true, + "version": "kibana", + "requiredPlugins": [ + "data", + "licensing", + "management" + ], + "optionalPlugins": [ + "security" + ] +} diff --git a/x-pack/legacy/plugins/beats_management/public/index.tsx b/x-pack/plugins/beats_management/public/application.tsx similarity index 64% rename from x-pack/legacy/plugins/beats_management/public/index.tsx rename to x-pack/plugins/beats_management/public/application.tsx index cebefabe77cdcd..6711e93895b628 100644 --- a/x-pack/legacy/plugins/beats_management/public/index.tsx +++ b/x-pack/plugins/beats_management/public/application.tsx @@ -7,26 +7,31 @@ import * as euiVars from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import ReactDOM from 'react-dom'; import { HashRouter } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; -import { I18nContext } from 'ui/i18n'; import { Provider as UnstatedProvider, Subscribe } from 'unstated'; -import { BASE_PATH } from '../common/constants'; import { Background } from './components/layouts/background'; import { BreadcrumbProvider } from './components/navigation/breadcrumb'; import { Breadcrumb } from './components/navigation/breadcrumb/breadcrumb'; import { BeatsContainer } from './containers/beats'; import { TagsContainer } from './containers/tags'; -import { compose } from './lib/compose/kibana'; import { FrontendLibs } from './lib/types'; import { AppRouter } from './router'; +import { services } from './kbn_services'; +import { + ManagementAppMountParams, + ManagementSectionId, +} from '../../../../src/plugins/management/public'; -async function startApp(libs: FrontendLibs) { - libs.framework.renderUIAtPath( - BASE_PATH, +export const renderApp = ( + { basePath, element, setBreadcrumbs }: ManagementAppMountParams, + libs: FrontendLibs +) => { + ReactDOM.render( - - + + @@ -44,30 +49,12 @@ async function startApp(libs: FrontendLibs) { - + , - libs.framework.versionGreaterThen('6.7.0') ? 'management' : 'self' + element ); - await libs.framework.waitUntilFrameworkReady(); - - if (libs.framework.licenseIsAtLeast('standard')) { - libs.framework.registerManagementSection({ - id: 'beats', - name: i18n.translate('xpack.beatsManagement.centralManagementSectionLabel', { - defaultMessage: 'Beats', - }), - iconName: 'logoBeats', - }); - - libs.framework.registerManagementUI({ - sectionId: 'beats', - name: i18n.translate('xpack.beatsManagement.centralManagementLinkLabel', { - defaultMessage: 'Central Management', - }), - basePath: BASE_PATH, - }); - } -} - -startApp(compose()); + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/plugins/beats_management/public/bootstrap.tsx b/x-pack/plugins/beats_management/public/bootstrap.tsx new file mode 100644 index 00000000000000..9a45be702e212a --- /dev/null +++ b/x-pack/plugins/beats_management/public/bootstrap.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FrontendLibs } from './lib/types'; +import { compose } from './lib/compose/kibana'; + +import { setServices } from './kbn_services'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { SecurityPluginSetup } from '../../security/public'; +import { CoreSetup } from '../../../../src/core/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { LicensingPluginSetup } from '../../licensing/public'; +import { BeatsManagementConfigType } from '../common'; + +async function startApp(libs: FrontendLibs, core: CoreSetup) { + await libs.framework.waitUntilFrameworkReady(); + + if (libs.framework.licenseIsAtLeast('standard')) { + const mount = async (params: any) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + setServices(coreStart, pluginsStart, params); + const { renderApp } = await import('./application'); + return renderApp(params, libs); + }; + + libs.framework.registerManagementUI(mount); + } +} + +interface SetupDeps { + management: ManagementSetup; + licensing: LicensingPluginSetup; + security?: SecurityPluginSetup; +} + +interface StartDeps { + data: DataPublicPluginStart; +} + +export const bootstrap = ( + core: CoreSetup, + plugins: SetupDeps, + config: BeatsManagementConfigType, + version: string +) => { + startApp( + compose({ + core, + config, + version, + ...plugins, + }), + core + ); +}; diff --git a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx b/x-pack/plugins/beats_management/public/components/autocomplete_field/index.tsx similarity index 99% rename from x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx rename to x-pack/plugins/beats_management/public/components/autocomplete_field/index.tsx index 70b7bd3df06629..107cb987f21079 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/index.tsx +++ b/x-pack/plugins/beats_management/public/components/autocomplete_field/index.tsx @@ -13,7 +13,7 @@ import { import React from 'react'; import styled from 'styled-components'; -import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; import { composeStateUpdaters } from '../../utils/typed_react'; import { SuggestionItem } from './suggestion_item'; diff --git a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx similarity index 97% rename from x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx rename to x-pack/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx index 690d471b306ab8..38f1f5b0255329 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx +++ b/x-pack/plugins/beats_management/public/components/autocomplete_field/suggestion_item.tsx @@ -9,7 +9,7 @@ import { tint } from 'polished'; import React from 'react'; import styled from 'styled-components'; -import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; interface SuggestionItemProps { isSelected?: boolean; diff --git a/x-pack/legacy/plugins/beats_management/public/components/config_list.tsx b/x-pack/plugins/beats_management/public/components/config_list.tsx similarity index 91% rename from x-pack/legacy/plugins/beats_management/public/components/config_list.tsx rename to x-pack/plugins/beats_management/public/components/config_list.tsx index 3ffd42c5201f93..9116f7a37ce927 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/config_list.tsx +++ b/x-pack/plugins/beats_management/public/components/config_list.tsx @@ -8,9 +8,9 @@ import { EuiBasicTable, EuiLink } from '@elastic/eui'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React from 'react'; -import { configBlockSchemas } from '../../common/config_schemas'; -import { translateConfigSchema } from '../../common/config_schemas_translations_map'; -import { ConfigurationBlock } from '../../common/domain_types'; +import { configBlockSchemas } from '../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; +import { ConfigurationBlock } from '../../../../legacy/plugins/beats_management/common/domain_types'; interface ComponentProps { configs: { diff --git a/x-pack/legacy/plugins/beats_management/public/components/enroll_beats.tsx b/x-pack/plugins/beats_management/public/components/enroll_beats.tsx similarity index 99% rename from x-pack/legacy/plugins/beats_management/public/components/enroll_beats.tsx rename to x-pack/plugins/beats_management/public/components/enroll_beats.tsx index ee97198211b560..f7a8ea413531f6 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/enroll_beats.tsx +++ b/x-pack/plugins/beats_management/public/components/enroll_beats.tsx @@ -20,7 +20,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { capitalize } from 'lodash'; import React from 'react'; -import { CMBeat } from '../../common/domain_types'; +import { CMBeat } from '../../../../legacy/plugins/beats_management/common/domain_types'; interface ComponentProps { /** Such as kibanas basePath, for use to generate command */ diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/code_editor.tsx b/x-pack/plugins/beats_management/public/components/inputs/code_editor.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/code_editor.tsx rename to x-pack/plugins/beats_management/public/components/inputs/code_editor.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/index.ts b/x-pack/plugins/beats_management/public/components/inputs/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/index.ts rename to x-pack/plugins/beats_management/public/components/inputs/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx b/x-pack/plugins/beats_management/public/components/inputs/input.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx rename to x-pack/plugins/beats_management/public/components/inputs/input.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/multi_input.tsx b/x-pack/plugins/beats_management/public/components/inputs/multi_input.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/multi_input.tsx rename to x-pack/plugins/beats_management/public/components/inputs/multi_input.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/password_input.tsx b/x-pack/plugins/beats_management/public/components/inputs/password_input.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/password_input.tsx rename to x-pack/plugins/beats_management/public/components/inputs/password_input.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/select.tsx b/x-pack/plugins/beats_management/public/components/inputs/select.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/inputs/select.tsx rename to x-pack/plugins/beats_management/public/components/inputs/select.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/layouts/background.tsx b/x-pack/plugins/beats_management/public/components/layouts/background.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/layouts/background.tsx rename to x-pack/plugins/beats_management/public/components/layouts/background.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/layouts/no_data.tsx b/x-pack/plugins/beats_management/public/components/layouts/no_data.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/layouts/no_data.tsx rename to x-pack/plugins/beats_management/public/components/layouts/no_data.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/layouts/primary.tsx b/x-pack/plugins/beats_management/public/components/layouts/primary.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/layouts/primary.tsx rename to x-pack/plugins/beats_management/public/components/layouts/primary.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/layouts/walkthrough.tsx b/x-pack/plugins/beats_management/public/components/layouts/walkthrough.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/layouts/walkthrough.tsx rename to x-pack/plugins/beats_management/public/components/layouts/walkthrough.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/loading.tsx b/x-pack/plugins/beats_management/public/components/loading.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/loading.tsx rename to x-pack/plugins/beats_management/public/components/loading.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx similarity index 90% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx index efe5eb25a0ac1c..13a0003b5ebdf8 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx +++ b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/breadcrumb.tsx @@ -5,7 +5,7 @@ */ import React, { Component } from 'react'; import { RouteProps } from 'react-router-dom'; -import { BASE_PATH } from '../../../../common/constants'; +import { BASE_PATH } from '../../../../../../legacy/plugins/beats_management/common/constants'; import { BreadcrumbConsumer } from './consumer'; import { Breadcrumb as BreadcrumbData, BreadcrumbContext } from './types'; @@ -53,7 +53,7 @@ export const Breadcrumb: React.FC = ({ title, path, parentBread {context => ( diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/consumer.tsx b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/consumer.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/consumer.tsx rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/consumer.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/context.tsx b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/context.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/context.tsx rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/context.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/index.ts b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/index.ts rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx similarity index 95% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx index f15e08c2ca230a..1dcceac5c710d8 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx +++ b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/provider.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Component, ReactElement } from 'react'; -import chrome from 'ui/chrome'; import { Provider } from './context'; import { Breadcrumb } from './types'; +import { services } from '../../../kbn_services'; interface ComponentProps { useGlobalBreadcrumbs: boolean; @@ -64,7 +64,7 @@ export class BreadcrumbProvider extends Component{this.props.children}; } diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/types.d.ts b/x-pack/plugins/beats_management/public/components/navigation/breadcrumb/types.d.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/breadcrumb/types.d.ts rename to x-pack/plugins/beats_management/public/components/navigation/breadcrumb/types.d.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/child_routes.tsx b/x-pack/plugins/beats_management/public/components/navigation/child_routes.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/child_routes.tsx rename to x-pack/plugins/beats_management/public/components/navigation/child_routes.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/navigation/connected_link.tsx b/x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/navigation/connected_link.tsx rename to x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/action_schema.ts b/x-pack/plugins/beats_management/public/components/table/action_schema.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/table/action_schema.ts rename to x-pack/plugins/beats_management/public/components/table/action_schema.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/controls/action_control.tsx b/x-pack/plugins/beats_management/public/components/table/controls/action_control.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/table/controls/action_control.tsx rename to x-pack/plugins/beats_management/public/components/table/controls/action_control.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/controls/index.ts b/x-pack/plugins/beats_management/public/components/table/controls/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/table/controls/index.ts rename to x-pack/plugins/beats_management/public/components/table/controls/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/controls/option_control.tsx b/x-pack/plugins/beats_management/public/components/table/controls/option_control.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/table/controls/option_control.tsx rename to x-pack/plugins/beats_management/public/components/table/controls/option_control.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx b/x-pack/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx similarity index 96% rename from x-pack/legacy/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx rename to x-pack/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx index cd75f882186c23..19acb1441df4c0 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx +++ b/x-pack/plugins/beats_management/public/components/table/controls/tag_badge_list.tsx @@ -13,7 +13,7 @@ import { EuiPopover, } from '@elastic/eui'; import React from 'react'; -import { TABLE_CONFIG } from '../../../../common/constants/table'; +import { TABLE_CONFIG } from '../../../../../../legacy/plugins/beats_management/common/constants/table'; import { TagBadge } from '../../tag/tag_badge'; import { AssignmentActionType } from '../index'; diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/index.ts b/x-pack/plugins/beats_management/public/components/table/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/table/index.ts rename to x-pack/plugins/beats_management/public/components/table/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx b/x-pack/plugins/beats_management/public/components/table/table.tsx similarity index 96% rename from x-pack/legacy/plugins/beats_management/public/components/table/table.tsx rename to x-pack/plugins/beats_management/public/components/table/table.tsx index 534da6541b683b..ae158e9d034767 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx +++ b/x-pack/plugins/beats_management/public/components/table/table.tsx @@ -8,8 +8,8 @@ import { EuiBasicTable, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eu import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; -import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; -import { TABLE_CONFIG } from '../../../common/constants'; +import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; +import { TABLE_CONFIG } from '../../../../../legacy/plugins/beats_management/common/constants'; import { AutocompleteField } from '../autocomplete_field/index'; import { ControlSchema } from './action_schema'; import { OptionControl } from './controls/option_control'; diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx similarity index 98% rename from x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx rename to x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx index 6fff7574c39e6e..65e0d19aa47700 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx +++ b/x-pack/plugins/beats_management/public/components/table/table_type_configs.tsx @@ -9,7 +9,10 @@ import { i18n } from '@kbn/i18n'; import { sortBy, uniq } from 'lodash'; import moment from 'moment'; import React from 'react'; -import { BeatTag, CMBeat } from '../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { ConnectedLink } from '../navigation/connected_link'; import { TagBadge } from '../tag'; diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/config_view/config_form.tsx b/x-pack/plugins/beats_management/public/components/tag/config_view/config_form.tsx similarity index 98% rename from x-pack/legacy/plugins/beats_management/public/components/tag/config_view/config_form.tsx rename to x-pack/plugins/beats_management/public/components/tag/config_view/config_form.tsx index 11336f230c8406..0469a623198134 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/tag/config_view/config_form.tsx +++ b/x-pack/plugins/beats_management/public/components/tag/config_view/config_form.tsx @@ -8,7 +8,10 @@ import { i18n } from '@kbn/i18n'; import Formsy from 'formsy-react'; import { get } from 'lodash'; import React from 'react'; -import { ConfigBlockSchema, ConfigurationBlock } from '../../../../common/domain_types'; +import { + ConfigBlockSchema, + ConfigurationBlock, +} from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { FormsyEuiCodeEditor, FormsyEuiFieldText, diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/config_view/index.tsx b/x-pack/plugins/beats_management/public/components/tag/config_view/index.tsx similarity index 94% rename from x-pack/legacy/plugins/beats_management/public/components/tag/config_view/index.tsx rename to x-pack/plugins/beats_management/public/components/tag/config_view/index.tsx index 20eb2b9c3fd0d9..4390e73b6fc063 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/tag/config_view/index.tsx +++ b/x-pack/plugins/beats_management/public/components/tag/config_view/index.tsx @@ -22,9 +22,9 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { configBlockSchemas } from '../../../../common/config_schemas'; -import { translateConfigSchema } from '../../../../common/config_schemas_translations_map'; -import { ConfigurationBlock } from '../../../../common/domain_types'; +import { configBlockSchemas } from '../../../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; +import { ConfigurationBlock } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { ConfigForm } from './config_form'; interface ComponentProps { diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/index.ts b/x-pack/plugins/beats_management/public/components/tag/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/components/tag/index.ts rename to x-pack/plugins/beats_management/public/components/tag/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx b/x-pack/plugins/beats_management/public/components/tag/tag_badge.tsx similarity index 93% rename from x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx rename to x-pack/plugins/beats_management/public/components/tag/tag_badge.tsx index 7fa0231cf34095..5880871da9f135 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_badge.tsx +++ b/x-pack/plugins/beats_management/public/components/tag/tag_badge.tsx @@ -6,7 +6,7 @@ import { EuiBadge, EuiBadgeProps } from '@elastic/eui'; import React from 'react'; -import { TABLE_CONFIG } from '../../../common/constants'; +import { TABLE_CONFIG } from '../../../../../legacy/plugins/beats_management/common/constants'; type TagBadgeProps = EuiBadgeProps & { maxIdRenderSize?: number; diff --git a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_edit.tsx b/x-pack/plugins/beats_management/public/components/tag/tag_edit.tsx similarity index 98% rename from x-pack/legacy/plugins/beats_management/public/components/tag/tag_edit.tsx rename to x-pack/plugins/beats_management/public/components/tag/tag_edit.tsx index 4912abec90682d..5ea4b643fb5a2a 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/tag/tag_edit.tsx +++ b/x-pack/plugins/beats_management/public/components/tag/tag_edit.tsx @@ -24,7 +24,11 @@ import { FormattedMessage } from '@kbn/i18n/react'; import 'brace/mode/yaml'; import 'brace/theme/github'; import React from 'react'; -import { BeatTag, CMBeat, ConfigurationBlock } from '../../../common/domain_types'; +import { + BeatTag, + CMBeat, + ConfigurationBlock, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { ConfigList } from '../config_list'; import { AssignmentActionType, BeatsTableType, Table, tagConfigActions } from '../table'; import { ConfigView } from './config_view'; diff --git a/x-pack/legacy/plugins/beats_management/public/containers/beats.ts b/x-pack/plugins/beats_management/public/containers/beats.ts similarity index 93% rename from x-pack/legacy/plugins/beats_management/public/containers/beats.ts rename to x-pack/plugins/beats_management/public/containers/beats.ts index 7340f6b56d6f65..1c2a337798b252 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/beats.ts +++ b/x-pack/plugins/beats_management/public/containers/beats.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { Container } from 'unstated'; -import { CMBeat } from './../../common/domain_types'; -import { BeatsTagAssignment } from './../../server/lib/adapters/beats/adapter_types'; +import { CMBeat } from '../../../../legacy/plugins/beats_management/common/domain_types'; +import { BeatsTagAssignment } from '../../../../legacy/plugins/beats_management/server/lib/adapters/beats/adapter_types'; import { FrontendLibs } from './../lib/types'; interface ContainerState { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/tags.ts b/x-pack/plugins/beats_management/public/containers/tags.ts similarity index 93% rename from x-pack/legacy/plugins/beats_management/public/containers/tags.ts rename to x-pack/plugins/beats_management/public/containers/tags.ts index 08af22f7cf4cc3..345d264878503b 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/tags.ts +++ b/x-pack/plugins/beats_management/public/containers/tags.ts @@ -5,7 +5,7 @@ */ import { Container } from 'unstated'; -import { BeatTag } from '../../common/domain_types'; +import { BeatTag } from '../../../../legacy/plugins/beats_management/common/domain_types'; import { FrontendLibs } from '../lib/types'; interface ContainerState { diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx similarity index 96% rename from x-pack/legacy/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx rename to x-pack/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx index 66d52b8dcc5dcb..007f1f9535bc45 100644 --- a/x-pack/legacy/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/beats_management/public/containers/with_kuery_autocompletion.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../src/plugins/data/public'; import { FrontendLibs } from '../lib/types'; import { RendererFunction } from '../utils/typed_react'; diff --git a/x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx b/x-pack/plugins/beats_management/public/containers/with_url_state.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/containers/with_url_state.tsx rename to x-pack/plugins/beats_management/public/containers/with_url_state.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/frontend_types.d.ts b/x-pack/plugins/beats_management/public/frontend_types.d.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/frontend_types.d.ts rename to x-pack/plugins/beats_management/public/frontend_types.d.ts diff --git a/x-pack/plugins/beats_management/public/index.ts b/x-pack/plugins/beats_management/public/index.ts new file mode 100644 index 00000000000000..9a3d1a9dc0f53d --- /dev/null +++ b/x-pack/plugins/beats_management/public/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { CoreSetup, Plugin, PluginInitializerContext } from '../../../../src/core/public'; + +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { SecurityPluginSetup } from '../../security/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { LicensingPluginSetup } from '../../licensing/public'; + +import { bootstrap } from './bootstrap'; +import { BeatsManagementConfigType } from '../common'; + +interface SetupDeps { + management: ManagementSetup; + licensing: LicensingPluginSetup; + security?: SecurityPluginSetup; +} + +interface StartDeps { + data: DataPublicPluginStart; +} + +class BeatsManagementPlugin implements Plugin { + constructor(private readonly initContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, plugins: SetupDeps) { + const config = this.initContext.config.get(); + bootstrap(core, plugins, config, this.initContext.env.packageInfo.version); + } + + public start() {} + public stop() {} +} + +export const plugin = (init: PluginInitializerContext) => new BeatsManagementPlugin(init); diff --git a/x-pack/plugins/beats_management/public/kbn_services.ts b/x-pack/plugins/beats_management/public/kbn_services.ts new file mode 100644 index 00000000000000..ca5c63802aac5f --- /dev/null +++ b/x-pack/plugins/beats_management/public/kbn_services.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from '../../../../src/core/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { ManagementAppMountParams } from '../../../../src/plugins/management/public'; + +export const services = { + I18nContext: (null as any) as CoreStart['i18n']['Context'], + setBreadcrumbs: (null as any) as ManagementAppMountParams['setBreadcrumbs'], + dataStart: (null as any) as DataPublicPluginStart, +}; + +export const setServices = ( + core: CoreStart, + plugins: { data: DataPublicPluginStart }, + params: ManagementAppMountParams +) => { + services.I18nContext = core.i18n.Context; + services.setBreadcrumbs = params.setBreadcrumbs; + services.dataStart = plugins.data; +}; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts b/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts similarity index 95% rename from x-pack/legacy/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts rename to x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts index 6776fb0b6f5add..6851020cdf97e8 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts +++ b/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { configBlockSchemas } from '../../../common/config_schemas'; -import { translateConfigSchema } from '../../../common/config_schemas_translations_map'; +import { configBlockSchemas } from '../../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; import { ConfigBlocksLib } from '../configuration_blocks'; import { MemoryConfigBlocksAdapter } from './../adapters/configuration_blocks/memory_config_blocks_adapter'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts similarity index 84% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts index 815b80e55fa116..1366894f78ddbb 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/beats/adapter_types.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CMBeat } from '../../../../common/domain_types'; -import { ReturnTypeBulkAction } from '../../../../common/return_types'; +import { CMBeat } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; +import { ReturnTypeBulkAction } from '../../../../../../legacy/plugins/beats_management/common/return_types'; export interface CMBeatsAdapter { get(id: string): Promise; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts similarity index 94% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts index 319e37e226c9e5..74fd07d5d3dff0 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/beats/memory_beats_adapter.ts @@ -5,8 +5,8 @@ */ import { omit } from 'lodash'; -import { CMBeat } from '../../../../common/domain_types'; -import { ReturnTypeBulkAction } from '../../../../common/return_types'; +import { CMBeat } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; +import { ReturnTypeBulkAction } from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { BeatsTagAssignment, CMBeatsAdapter } from './adapter_types'; export class MemoryBeatsAdapter implements CMBeatsAdapter { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts similarity index 92% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts index b2e11461007fd2..880af83bfb3f21 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/beats/rest_beats_adapter.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CMBeat } from '../../../../common/domain_types'; +import { CMBeat } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { ReturnTypeBulkAction, ReturnTypeGet, ReturnTypeList, ReturnTypeUpdate, -} from '../../../../common/return_types'; +} from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { RestAPIAdapter } from '../rest_api/adapter_types'; import { BeatsTagAssignment, CMBeatsAdapter } from './adapter_types'; export class RestBeatsAdapter implements CMBeatsAdapter { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts similarity index 67% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts index e8e2d5cd6b430d..413cf32529b2f5 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/adapter_types.ts @@ -3,8 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ConfigurationBlock } from '../../../../common/domain_types'; -import { ReturnTypeBulkUpsert, ReturnTypeList } from '../../../../common/return_types'; +import { ConfigurationBlock } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; +import { + ReturnTypeBulkUpsert, + ReturnTypeList, +} from '../../../../../../legacy/plugins/beats_management/common/return_types'; export interface FrontendConfigBlocksAdapter { upsert(blocks: ConfigurationBlock[]): Promise; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts similarity index 84% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts index b1405cfbf2e60d..ab36c7fbbae0e7 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/memory_config_blocks_adapter.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ConfigurationBlock } from '../../../../common/domain_types'; -import { ReturnTypeBulkUpsert, ReturnTypeList } from '../../../../common/return_types'; +import { ConfigurationBlock } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; +import { + ReturnTypeBulkUpsert, + ReturnTypeList, +} from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { FrontendConfigBlocksAdapter } from './adapter_types'; export class MemoryConfigBlocksAdapter implements FrontendConfigBlocksAdapter { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts similarity index 86% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts index be501a5e951ba0..640da59ea572dc 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/configuration_blocks/rest_config_blocks_adapter.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ConfigurationBlock } from '../../../../common/domain_types'; +import { ConfigurationBlock } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { ReturnTypeBulkDelete, ReturnTypeBulkUpsert, ReturnTypeList, -} from '../../../../common/return_types'; +} from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { RestAPIAdapter } from '../rest_api/adapter_types'; import { FrontendConfigBlocksAdapter } from './adapter_types'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts similarity index 85% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts index 6e4665fb130de1..16339c8eac0038 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/adapter_types.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; export interface ElasticsearchAdapter { convertKueryToEsQuery: (kuery: string) => Promise; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts similarity index 91% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts rename to x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts index fc4daf3df60b2e..bda157cd7f12e2 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/memory.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { QuerySuggestion } from '../../../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../../../src/plugins/data/public'; import { ElasticsearchAdapter } from './adapter_types'; export class MemoryElasticsearchAdapter implements ElasticsearchAdapter { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts similarity index 85% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts rename to x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts index 06e6fac0d75c41..4473e015d9d742 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts @@ -5,9 +5,9 @@ */ import { isEmpty } from 'lodash'; -import { npStart } from 'ui/new_platform'; import { ElasticsearchAdapter } from './adapter_types'; -import { QuerySuggestion, esKuery } from '../../../../../../../../src/plugins/data/public'; +import { QuerySuggestion, esKuery } from '../../../../../../../src/plugins/data/public'; +import { services } from '../../../kbn_services'; export class RestElasticsearchAdapter implements ElasticsearchAdapter { private cachedIndexPattern: any = null; @@ -35,7 +35,7 @@ export class RestElasticsearchAdapter implements ElasticsearchAdapter { const indexPattern = await this.getIndexPattern(); return ( - (await npStart.plugins.data.autocomplete.getQuerySuggestions({ + (await services.dataStart.autocomplete.getQuerySuggestions({ language: 'kuery', indexPatterns: [indexPattern], boolFilter: [], @@ -50,7 +50,7 @@ export class RestElasticsearchAdapter implements ElasticsearchAdapter { if (this.cachedIndexPattern) { return this.cachedIndexPattern; } - const res = await npStart.plugins.data.indexPatterns.getFieldsForWildcard({ + const res = await services.dataStart.indexPatterns.getFieldsForWildcard({ pattern: this.indexPatternName, }); if (isEmpty(res.fields)) { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts similarity index 61% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts index 96b64e8171af50..9d7a1954b60d95 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/adapter_types.ts @@ -7,7 +7,8 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import * as t from 'io-ts'; -import { LICENSES } from './../../../../common/constants/security'; +import { LICENSES } from '../../../../../../legacy/plugins/beats_management/common/constants/security'; +import { RegisterManagementAppArgs } from '../../../../../../../src/plugins/management/public'; export interface FrameworkAdapter { // Instance vars @@ -16,24 +17,7 @@ export interface FrameworkAdapter { currentUser: FrameworkUser; // Methods waitUntilFrameworkReady(): Promise; - renderUIAtPath( - path: string, - component: React.ReactElement, - toController: 'management' | 'self' - ): void; - registerManagementSection(settings: { - id?: string; - name: string; - iconName: string; - order?: number; - }): void; - registerManagementUI(settings: { - sectionId?: string; - name: string; - basePath: string; - visable?: boolean; - order?: number; - }): void; + registerManagementUI(mount: RegisterManagementAppArgs['mount']): void; } export const RuntimeFrameworkInfo = t.type({ @@ -56,23 +40,6 @@ export const RuntimeFrameworkInfo = t.type({ export interface FrameworkInfo extends t.TypeOf {} -interface ManagementSection { - register( - sectionId: string, - options: { - visible: boolean; - display: string; - order: number; - url: string; - } - ): void; -} -export interface ManagementAPI { - getSection(sectionId: string): ManagementSection; - hasItem(sectionId: string): boolean; - register(sectionId: string, options: { display: string; icon: string; order: number }): void; -} - export const RuntimeFrameworkUser = t.interface( { username: t.string, diff --git a/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts new file mode 100644 index 00000000000000..1ae21a561950d3 --- /dev/null +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable max-classes-per-file */ +import { IScope } from 'angular'; +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { isLeft } from 'fp-ts/lib/Either'; +import { first } from 'rxjs/operators'; +import { i18n } from '@kbn/i18n'; +import { SecurityPluginSetup } from '../../../../../security/public'; +import { BufferedKibanaServiceCall, KibanaAdapterServiceRefs, KibanaUIConfig } from '../../types'; +import { + FrameworkAdapter, + FrameworkInfo, + FrameworkUser, + RuntimeFrameworkInfo, + RuntimeFrameworkUser, +} from './adapter_types'; +import { + ManagementSetup, + RegisterManagementAppArgs, + ManagementSectionId, +} from '../../../../../../../src/plugins/management/public'; +import { LicensingPluginSetup } from '../../../../../licensing/public'; +import { BeatsManagementConfigType } from '../../../../common'; + +export class KibanaFrameworkAdapter implements FrameworkAdapter { + public get info() { + if (this.xpackInfo) { + return this.xpackInfo; + } else { + throw new Error('framework adapter must have init called before anything else'); + } + } + + public get currentUser() { + return this.shieldUser!; + } + private xpackInfo: FrameworkInfo | null = null; + private adapterService: KibanaAdapterServiceProvider; + private shieldUser: FrameworkUser | null = null; + constructor( + private readonly PLUGIN_ID: string, + private readonly management: ManagementSetup, + private readonly getBasePath: () => string, + private readonly licensing: LicensingPluginSetup, + private readonly securitySetup: SecurityPluginSetup | undefined, + private readonly config: BeatsManagementConfigType, + public readonly version: string + ) { + this.adapterService = new KibanaAdapterServiceProvider(); + } + + public setUISettings = (key: string, value: any) => { + this.adapterService.callOrBuffer(({ config }) => { + config.set(key, value); + }); + }; + + public async waitUntilFrameworkReady(): Promise { + const license = await this.licensing.license$.pipe(first()).toPromise(); + let xpackInfoUnpacked: FrameworkInfo; + + try { + xpackInfoUnpacked = { + basePath: this.getBasePath(), + license: { + type: license.type ?? 'oss', + expired: !license.isActive, + expiry_date_in_millis: license.expiryDateInMillis ?? -1, + }, + security: { + enabled: license.getFeature('security').isEnabled, + available: license.getFeature('security').isAvailable, + }, + settings: this.config, + }; + } catch (e) { + throw new Error(`Unexpected data structure from xpackInfoService, ${JSON.stringify(e)}`); + } + + const assertData = RuntimeFrameworkInfo.decode(xpackInfoUnpacked); + if (isLeft(assertData)) { + throw new Error( + `Error parsing xpack info in ${this.PLUGIN_ID}, ${PathReporter.report(assertData)[0]}` + ); + } + this.xpackInfo = xpackInfoUnpacked; + + try { + this.shieldUser = (await this.securitySetup?.authc.getCurrentUser()) || null; + const assertUser = RuntimeFrameworkUser.decode(this.shieldUser); + + if (isLeft(assertUser)) { + throw new Error( + `Error parsing user info in ${this.PLUGIN_ID}, ${PathReporter.report(assertUser)[0]}` + ); + } + } catch (e) { + this.shieldUser = null; + } + } + + public registerManagementUI(mount: RegisterManagementAppArgs['mount']) { + const section = this.management.sections.getSection(ManagementSectionId.Ingest); + section.registerApp({ + id: 'beats_management', + title: i18n.translate('xpack.beatsManagement.centralManagementLinkLabel', { + defaultMessage: 'Beats Central Management', + }), + order: 2, + mount, + }); + } +} + +class KibanaAdapterServiceProvider { + public serviceRefs: KibanaAdapterServiceRefs | null = null; + public bufferedCalls: Array> = []; + + public $get($rootScope: IScope, config: KibanaUIConfig) { + this.serviceRefs = { + config, + rootScope: $rootScope, + }; + + this.applyBufferedCalls(this.bufferedCalls); + + return this; + } + + public callOrBuffer(serviceCall: (serviceRefs: KibanaAdapterServiceRefs) => void) { + if (this.serviceRefs !== null) { + this.applyBufferedCalls([serviceCall]); + } else { + this.bufferedCalls.push(serviceCall); + } + } + + public applyBufferedCalls( + bufferedCalls: Array> + ) { + if (!this.serviceRefs) { + return; + } + + this.serviceRefs.rootScope.$apply(() => { + bufferedCalls.forEach(serviceCall => { + if (!this.serviceRefs) { + return; + } + return serviceCall(this.serviceRefs); + }); + }); + } +} diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts similarity index 69% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts index 9045c7ded2adaa..52d185e0d7dc97 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/framework/testing_framework_adapter.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as React from 'react'; import { FrameworkAdapter, FrameworkInfo, FrameworkUser } from './adapter_types'; export class TestingFrameworkAdapter implements FrameworkAdapter { @@ -40,30 +39,7 @@ export class TestingFrameworkAdapter implements FrameworkAdapter { return; } - public renderUIAtPath( - path: string, - component: React.ReactElement, - toController: 'management' | 'self' = 'self' - ) { - throw new Error('not yet implamented'); - } - - public registerManagementSection(settings: { - id?: string; - name: string; - iconName: string; - order?: number; - }) { - throw new Error('not yet implamented'); - } - - public registerManagementUI(settings: { - sectionId?: string; - name: string; - basePath: string; - visable?: boolean; - order?: number; - }) { + public registerManagementUI(settings: { sectionId?: string; name: string; order?: number }) { throw new Error('not yet implamented'); } } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/rest_api/adapter_types.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/rest_api/adapter_types.ts diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/axios_rest_api_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/rest_api/axios_rest_api_adapter.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/axios_rest_api_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/rest_api/axios_rest_api_adapter.ts diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/node_axios_api_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/rest_api/node_axios_api_adapter.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/rest_api/node_axios_api_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/rest_api/node_axios_api_adapter.ts diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts similarity index 82% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts index 5030b1704b1d7a..b0ddf0309232ab 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/tags/adapter_types.ts @@ -3,7 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { BeatTag, CMBeat } from '../../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../../legacy/plugins/beats_management/common/domain_types'; export interface CMTagsAdapter { getTagsWithIds(tagIds: string[]): Promise; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts similarity index 91% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts index e26c6c9eb8099e..357790c34ee587 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/tags/memory_tags_adapter.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { BeatTag, CMBeat } from '../../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { CMTagsAdapter } from './adapter_types'; export class MemoryTagsAdapter implements CMTagsAdapter { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts similarity index 90% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts index 190c9e265463dc..4f6852cb6a007a 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/tags/rest_tags_adapter.ts @@ -5,13 +5,16 @@ */ import { uniq } from 'lodash'; -import { BeatTag, CMBeat } from '../../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { ReturnTypeBulkDelete, ReturnTypeBulkGet, ReturnTypeList, ReturnTypeUpsert, -} from '../../../../common/return_types'; +} from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { RestAPIAdapter } from '../rest_api/adapter_types'; import { CMTagsAdapter } from './adapter_types'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/adapter_types.ts b/x-pack/plugins/beats_management/public/lib/adapters/tokens/adapter_types.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/adapter_types.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tokens/adapter_types.ts diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/memory_tokens_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/tokens/memory_tokens_adapter.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/memory_tokens_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tokens/memory_tokens_adapter.ts diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts b/x-pack/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts similarity index 87% rename from x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts rename to x-pack/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts index 92cfcc935ad9b0..7bdea96db0f2f0 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts +++ b/x-pack/plugins/beats_management/public/lib/adapters/tokens/rest_tokens_adapter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ReturnTypeBulkCreate } from '../../../../common/return_types'; +import { ReturnTypeBulkCreate } from '../../../../../../legacy/plugins/beats_management/common/return_types'; import { RestAPIAdapter } from '../rest_api/adapter_types'; import { CMTokensAdapter } from './adapter_types'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/beats.ts b/x-pack/plugins/beats_management/public/lib/beats.ts similarity index 92% rename from x-pack/legacy/plugins/beats_management/public/lib/beats.ts rename to x-pack/plugins/beats_management/public/lib/beats.ts index 5432a39953b84c..a6cfe0b88dcf8f 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/beats.ts +++ b/x-pack/plugins/beats_management/public/lib/beats.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ReturnTypeBulkAction } from '../../common/return_types'; -import { CMBeat } from './../../common/domain_types'; +import { ReturnTypeBulkAction } from '../../../../legacy/plugins/beats_management/common/return_types'; +import { CMBeat } from '../../../../legacy/plugins/beats_management/common/domain_types'; import { BeatsTagAssignment, CMBeatsAdapter } from './adapters/beats/adapter_types'; import { ElasticsearchLib } from './elasticsearch'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts b/x-pack/plugins/beats_management/public/lib/compose/kibana.ts similarity index 59% rename from x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts rename to x-pack/plugins/beats_management/public/lib/compose/kibana.ts index 2ebda89ba13fd6..d750417640f8c7 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts +++ b/x-pack/plugins/beats_management/public/lib/compose/kibana.ts @@ -5,16 +5,9 @@ */ import { camelCase } from 'lodash'; -// @ts-ignore not typed yet -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import 'ui/autoload/all'; -import chrome from 'ui/chrome'; -// @ts-ignore not typed yet -import { management } from 'ui/management'; -import routes from 'ui/routes'; -import { configBlockSchemas } from '../../../common/config_schemas'; -import { translateConfigSchema } from '../../../common/config_schemas_translations_map'; -import { INDEX_NAMES } from '../../../common/constants/index_names'; +import { configBlockSchemas } from '../../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; +import { INDEX_NAMES } from '../../../../../legacy/plugins/beats_management/common/constants/index_names'; import { RestBeatsAdapter } from '../adapters/beats/rest_beats_adapter'; import { RestConfigBlocksAdapter } from '../adapters/configuration_blocks/rest_config_blocks_adapter'; import { RestElasticsearchAdapter } from '../adapters/elasticsearch/rest'; @@ -27,14 +20,32 @@ import { ConfigBlocksLib } from '../configuration_blocks'; import { ElasticsearchLib } from '../elasticsearch'; import { TagsLib } from '../tags'; import { FrontendLibs } from '../types'; -import { PLUGIN } from './../../../common/constants/plugin'; +import { PLUGIN } from '../../../../../legacy/plugins/beats_management/common/constants/plugin'; import { FrameworkLib } from './../framework'; +import { ManagementSetup } from '../../../../../../src/plugins/management/public'; +import { SecurityPluginSetup } from '../../../../security/public'; +import { CoreSetup } from '../../../../../../src/core/public'; +import { LicensingPluginSetup } from '../../../../licensing/public'; +import { BeatsManagementConfigType } from '../../../common'; -// A super early spot in kibana loading that we can use to hook before most other things -const onKibanaReady = chrome.dangerouslyGetActiveInjector; +interface ComposeDeps { + core: CoreSetup; + management: ManagementSetup; + licensing: LicensingPluginSetup; + config: BeatsManagementConfigType; + version: string; + security?: SecurityPluginSetup; +} -export function compose(): FrontendLibs { - const api = new AxiosRestAPIAdapter(chrome.getXsrfToken(), chrome.getBasePath()); +export function compose({ + core, + management, + licensing, + config, + version, + security, +}: ComposeDeps): FrontendLibs { + const api = new AxiosRestAPIAdapter(version, core.http.basePath.get()); const esAdapter = new RestElasticsearchAdapter(INDEX_NAMES.BEATS); const elasticsearchLib = new ElasticsearchLib(esAdapter); const configBlocks = new ConfigBlocksLib( @@ -49,11 +60,11 @@ export function compose(): FrontendLibs { new KibanaFrameworkAdapter( camelCase(PLUGIN.ID), management, - routes, - chrome.getBasePath, - onKibanaReady, - xpackInfo, - chrome.getKibanaVersion() + core.http.basePath.get, + licensing, + security, + config, + version ) ); diff --git a/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts b/x-pack/plugins/beats_management/public/lib/compose/scripts.ts similarity index 91% rename from x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts rename to x-pack/plugins/beats_management/public/lib/compose/scripts.ts index 83129384a77dfa..093d618ba8d8b1 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/compose/scripts.ts +++ b/x-pack/plugins/beats_management/public/lib/compose/scripts.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { configBlockSchemas } from '../../../common/config_schemas'; -import { translateConfigSchema } from '../../../common/config_schemas_translations_map'; +import { configBlockSchemas } from '../../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; import { RestBeatsAdapter } from '../adapters/beats/rest_beats_adapter'; import { RestConfigBlocksAdapter } from '../adapters/configuration_blocks/rest_config_blocks_adapter'; import { MemoryElasticsearchAdapter } from '../adapters/elasticsearch/memory'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/configuration_blocks.ts b/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts similarity index 97% rename from x-pack/legacy/plugins/beats_management/public/lib/configuration_blocks.ts rename to x-pack/plugins/beats_management/public/lib/configuration_blocks.ts index c3b21b5df5175e..ffb431da1f43e3 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/configuration_blocks.ts +++ b/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts @@ -6,7 +6,10 @@ import yaml from 'js-yaml'; import { get, has, omit, set } from 'lodash'; -import { ConfigBlockSchema, ConfigurationBlock } from '../../common/domain_types'; +import { + ConfigBlockSchema, + ConfigurationBlock, +} from '../../../../legacy/plugins/beats_management/common/domain_types'; import { FrontendConfigBlocksAdapter } from './adapters/configuration_blocks/adapter_types'; export class ConfigBlocksLib { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/elasticsearch.ts b/x-pack/plugins/beats_management/public/lib/elasticsearch.ts similarity index 96% rename from x-pack/legacy/plugins/beats_management/public/lib/elasticsearch.ts rename to x-pack/plugins/beats_management/public/lib/elasticsearch.ts index 82576bff2cbfdc..a9ca69c44c97d4 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/elasticsearch.ts +++ b/x-pack/plugins/beats_management/public/lib/elasticsearch.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { QuerySuggestion } from '../../../../../../src/plugins/data/public'; +import { QuerySuggestion } from '../../../../../src/plugins/data/public'; import { ElasticsearchAdapter } from './adapters/elasticsearch/adapter_types'; interface HiddenFields { diff --git a/x-pack/legacy/plugins/beats_management/public/lib/framework.ts b/x-pack/plugins/beats_management/public/lib/framework.ts similarity index 88% rename from x-pack/legacy/plugins/beats_management/public/lib/framework.ts rename to x-pack/plugins/beats_management/public/lib/framework.ts index 141aa7168083d1..9e4271c683415d 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/framework.ts +++ b/x-pack/plugins/beats_management/public/lib/framework.ts @@ -5,13 +5,14 @@ */ import { difference, get } from 'lodash'; -import { LICENSES, LicenseType } from '../../common/constants/security'; +import { + LICENSES, + LicenseType, +} from '../../../../legacy/plugins/beats_management/common/constants/security'; import { FrameworkAdapter } from './adapters/framework/adapter_types'; export class FrameworkLib { public waitUntilFrameworkReady = this.adapter.waitUntilFrameworkReady.bind(this.adapter); - public renderUIAtPath = this.adapter.renderUIAtPath.bind(this.adapter); - public registerManagementSection = this.adapter.registerManagementSection.bind(this.adapter); public registerManagementUI = this.adapter.registerManagementUI.bind(this.adapter); constructor(private readonly adapter: FrameworkAdapter) {} diff --git a/x-pack/legacy/plugins/beats_management/public/lib/tags.ts b/x-pack/plugins/beats_management/public/lib/tags.ts similarity index 93% rename from x-pack/legacy/plugins/beats_management/public/lib/tags.ts rename to x-pack/plugins/beats_management/public/lib/tags.ts index 86562be3ff989e..2d67edf7e347e7 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/tags.ts +++ b/x-pack/plugins/beats_management/public/lib/tags.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import uuidv4 from 'uuid/v4'; -import { BeatTag, CMBeat } from '../../common/domain_types'; +import { BeatTag, CMBeat } from '../../../../legacy/plugins/beats_management/common/domain_types'; import { CMTagsAdapter } from './adapters/tags/adapter_types'; import { ElasticsearchLib } from './elasticsearch'; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/types.ts b/x-pack/plugins/beats_management/public/lib/types.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/lib/types.ts rename to x-pack/plugins/beats_management/public/lib/types.ts diff --git a/x-pack/legacy/plugins/beats_management/public/pages/__404.tsx b/x-pack/plugins/beats_management/public/pages/__404.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/__404.tsx rename to x-pack/plugins/beats_management/public/pages/__404.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx b/x-pack/plugins/beats_management/public/pages/beat/details.tsx similarity index 93% rename from x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx rename to x-pack/plugins/beats_management/public/pages/beat/details.tsx index 3952b44f82561d..6fae3a5efc71fa 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx +++ b/x-pack/plugins/beats_management/public/pages/beat/details.tsx @@ -19,10 +19,14 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { get } from 'lodash'; import React from 'react'; -import { configBlockSchemas } from '../../../common/config_schemas'; -import { translateConfigSchema } from '../../../common/config_schemas_translations_map'; -import { TABLE_CONFIG } from '../../../common/constants'; -import { BeatTag, CMBeat, ConfigurationBlock } from '../../../common/domain_types'; +import { configBlockSchemas } from '../../../../../legacy/plugins/beats_management/common/config_schemas'; +import { translateConfigSchema } from '../../../../../legacy/plugins/beats_management/common/config_schemas_translations_map'; +import { TABLE_CONFIG } from '../../../../../legacy/plugins/beats_management/common/constants'; +import { + BeatTag, + CMBeat, + ConfigurationBlock, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { Breadcrumb } from '../../components/navigation/breadcrumb'; import { ConnectedLink } from '../../components/navigation/connected_link'; import { TagBadge } from '../../components/tag'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/beat/index.tsx b/x-pack/plugins/beats_management/public/pages/beat/index.tsx similarity index 98% rename from x-pack/legacy/plugins/beats_management/public/pages/beat/index.tsx rename to x-pack/plugins/beats_management/public/pages/beat/index.tsx index 80590febc95be2..bea84377d2ab85 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/beat/index.tsx +++ b/x-pack/plugins/beats_management/public/pages/beat/index.tsx @@ -17,7 +17,7 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import moment from 'moment'; import React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; -import { CMBeat } from '../../../common/domain_types'; +import { CMBeat } from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { PrimaryLayout } from '../../components/layouts/primary'; import { Breadcrumb } from '../../components/navigation/breadcrumb'; import { ChildRoutes } from '../../components/navigation/child_routes'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/beat/tags.tsx b/x-pack/plugins/beats_management/public/pages/beat/tags.tsx similarity index 94% rename from x-pack/legacy/plugins/beats_management/public/pages/beat/tags.tsx rename to x-pack/plugins/beats_management/public/pages/beat/tags.tsx index 672c0d89bb0028..5a65473b25a24b 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/beat/tags.tsx +++ b/x-pack/plugins/beats_management/public/pages/beat/tags.tsx @@ -7,7 +7,10 @@ import { EuiGlobalToastList } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { BeatTag, CMBeat } from '../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { Breadcrumb } from '../../components/navigation/breadcrumb'; import { BeatDetailTagsTable, Table } from '../../components/table'; import { FrontendLibs } from '../../lib/types'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/error/enforce_security.tsx b/x-pack/plugins/beats_management/public/pages/error/enforce_security.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/error/enforce_security.tsx rename to x-pack/plugins/beats_management/public/pages/error/enforce_security.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/error/invalid_license.tsx b/x-pack/plugins/beats_management/public/pages/error/invalid_license.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/error/invalid_license.tsx rename to x-pack/plugins/beats_management/public/pages/error/invalid_license.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/error/no_access.tsx b/x-pack/plugins/beats_management/public/pages/error/no_access.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/error/no_access.tsx rename to x-pack/plugins/beats_management/public/pages/error/no_access.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/index.ts b/x-pack/plugins/beats_management/public/pages/index.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/index.ts rename to x-pack/plugins/beats_management/public/pages/index.ts diff --git a/x-pack/legacy/plugins/beats_management/public/pages/overview/configuration_tags.tsx b/x-pack/plugins/beats_management/public/pages/overview/configuration_tags.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/overview/configuration_tags.tsx rename to x-pack/plugins/beats_management/public/pages/overview/configuration_tags.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/overview/enrolled_beats.tsx b/x-pack/plugins/beats_management/public/pages/overview/enrolled_beats.tsx similarity index 99% rename from x-pack/legacy/plugins/beats_management/public/pages/overview/enrolled_beats.tsx rename to x-pack/plugins/beats_management/public/pages/overview/enrolled_beats.tsx index 635b9f03e8b388..5151f501ddba1f 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/overview/enrolled_beats.tsx +++ b/x-pack/plugins/beats_management/public/pages/overview/enrolled_beats.tsx @@ -19,7 +19,10 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { flatten, sortBy } from 'lodash'; import moment from 'moment'; import React from 'react'; -import { BeatTag, CMBeat } from '../../../common/domain_types'; +import { + BeatTag, + CMBeat, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { EnrollBeat } from '../../components/enroll_beats'; import { Breadcrumb } from '../../components/navigation/breadcrumb'; import { BeatsTableType, Table } from '../../components/table'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/overview/index.tsx b/x-pack/plugins/beats_management/public/pages/overview/index.tsx similarity index 97% rename from x-pack/legacy/plugins/beats_management/public/pages/overview/index.tsx rename to x-pack/plugins/beats_management/public/pages/overview/index.tsx index 57b007753491ce..4df1a7d0654696 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/overview/index.tsx +++ b/x-pack/plugins/beats_management/public/pages/overview/index.tsx @@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { Subscribe } from 'unstated'; -import { CMBeat } from '../../../common/domain_types'; +import { CMBeat } from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { PrimaryLayout } from '../../components/layouts/primary'; import { ChildRoutes } from '../../components/navigation/child_routes'; import { BeatsContainer } from '../../containers/beats'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/tag/create.tsx b/x-pack/plugins/beats_management/public/pages/tag/create.tsx similarity index 95% rename from x-pack/legacy/plugins/beats_management/public/pages/tag/create.tsx rename to x-pack/plugins/beats_management/public/pages/tag/create.tsx index 326c529af3c616..65fe8bf5d52c15 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/tag/create.tsx +++ b/x-pack/plugins/beats_management/public/pages/tag/create.tsx @@ -12,8 +12,11 @@ import 'brace/mode/yaml'; import 'brace/theme/github'; import { isEqual } from 'lodash'; import React from 'react'; -import { UNIQUENESS_ENFORCING_TYPES } from '../../../common/constants/configuration_blocks'; -import { BeatTag, ConfigurationBlock } from '../../../common/domain_types'; +import { UNIQUENESS_ENFORCING_TYPES } from '../../../../../legacy/plugins/beats_management/common/constants/configuration_blocks'; +import { + BeatTag, + ConfigurationBlock, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { PrimaryLayout } from '../../components/layouts/primary'; import { TagEdit } from '../../components/tag'; import { AppPageProps } from '../../frontend_types'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/tag/edit.tsx b/x-pack/plugins/beats_management/public/pages/tag/edit.tsx similarity index 96% rename from x-pack/legacy/plugins/beats_management/public/pages/tag/edit.tsx rename to x-pack/plugins/beats_management/public/pages/tag/edit.tsx index 553128df76baa3..918ae0bb6fcb2d 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/tag/edit.tsx +++ b/x-pack/plugins/beats_management/public/pages/tag/edit.tsx @@ -10,8 +10,12 @@ import 'brace/mode/yaml'; import 'brace/theme/github'; import { flatten } from 'lodash'; import React from 'react'; -import { UNIQUENESS_ENFORCING_TYPES } from '../../../common/constants'; -import { BeatTag, CMBeat, ConfigurationBlock } from '../../../common/domain_types'; +import { UNIQUENESS_ENFORCING_TYPES } from '../../../../../legacy/plugins/beats_management/common/constants'; +import { + BeatTag, + CMBeat, + ConfigurationBlock, +} from '../../../../../legacy/plugins/beats_management/common/domain_types'; import { PrimaryLayout } from '../../components/layouts/primary'; import { TagEdit } from '../../components/tag'; import { AppPageProps } from '../../frontend_types'; diff --git a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/beat.tsx b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/beat.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/beat.tsx rename to x-pack/plugins/beats_management/public/pages/walkthrough/initial/beat.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx similarity index 97% rename from x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx rename to x-pack/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx index 93489f5223ef8e..71f0b8161131f1 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx +++ b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/finish.tsx @@ -6,7 +6,7 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiPageContent } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React from 'react'; -import { CMBeat } from '../../../../common/domain_types'; +import { CMBeat } from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { AppPageProps } from '../../../frontend_types'; interface PageState { diff --git a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/index.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/index.tsx rename to x-pack/plugins/beats_management/public/pages/walkthrough/initial/index.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx similarity index 97% rename from x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx rename to x-pack/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx index 8dc3afeb06c6bf..525873e1dd64bd 100644 --- a/x-pack/legacy/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx +++ b/x-pack/plugins/beats_management/public/pages/walkthrough/initial/tag.tsx @@ -9,7 +9,10 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { isEqual } from 'lodash'; import React, { Component } from 'react'; import uuidv4 from 'uuid/v4'; -import { BeatTag, ConfigurationBlock } from '../../../../common/domain_types'; +import { + BeatTag, + ConfigurationBlock, +} from '../../../../../../legacy/plugins/beats_management/common/domain_types'; import { TagEdit } from '../../../components/tag/tag_edit'; import { AppPageProps } from '../../../frontend_types'; interface PageState { diff --git a/x-pack/legacy/plugins/beats_management/public/router.tsx b/x-pack/plugins/beats_management/public/router.tsx similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/router.tsx rename to x-pack/plugins/beats_management/public/router.tsx diff --git a/x-pack/legacy/plugins/beats_management/public/utils/random_eui_color.ts b/x-pack/plugins/beats_management/public/utils/random_eui_color.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/utils/random_eui_color.ts rename to x-pack/plugins/beats_management/public/utils/random_eui_color.ts diff --git a/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts b/x-pack/plugins/beats_management/public/utils/typed_react.ts similarity index 100% rename from x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts rename to x-pack/plugins/beats_management/public/utils/typed_react.ts diff --git a/x-pack/plugins/beats_management/server/index.ts b/x-pack/plugins/beats_management/server/index.ts new file mode 100644 index 00000000000000..607fb0ab2725d3 --- /dev/null +++ b/x-pack/plugins/beats_management/server/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { beatsManagementConfigSchema } from '../common'; + +export const config = { + schema: beatsManagementConfigSchema, + + exposeToBrowser: { + defaultUserRoles: true, + encryptionKey: true, + enrollmentTokensTtlInSeconds: true, + }, +}; + +export const plugin = () => ({ + setup() {}, + start() {}, + stop() {}, +}); diff --git a/x-pack/plugins/canvas/public/application.tsx b/x-pack/plugins/canvas/public/application.tsx index 9c2aa821be2d53..d97e4944da13af 100644 --- a/x-pack/plugins/canvas/public/application.tsx +++ b/x-pack/plugins/canvas/public/application.tsx @@ -27,9 +27,6 @@ import { getDocumentationLinks } from './lib/documentation_links'; import { HelpMenu } from './components/help_menu/help_menu'; import { createStore, destroyStore } from './store'; -import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../src/plugins/ui_actions/public'; -/* eslint-disable */ -import { ACTION_VALUE_CLICK } from '../../../../src/plugins/data/public/actions/value_click_action'; /* eslint-enable */ import { init as initStatsReporter } from './lib/ui_metric'; @@ -45,16 +42,6 @@ import './style/index.scss'; const { ReadOnlyBadge: strings } = CapabilitiesStrings; -let restoreAction: ActionByType | undefined; -const emptyAction = { - id: 'empty-action', - type: '', - getDisplayName: () => 'empty action', - getIconType: () => undefined, - isCompatible: async () => true, - execute: async () => undefined, -} as ActionByType; - export const renderApp = ( coreStart: CoreStart, plugins: CanvasStartDeps, @@ -134,17 +121,6 @@ export const initializeCanvas = async ( }, }); - // TODO: We need this to disable the filtering modal from popping up in lens embeds until - // they honor the disableTriggers parameter - const action = startPlugins.uiActions.getAction(ACTION_VALUE_CLICK); - - if (action) { - restoreAction = action; - - startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, action.id); - startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, emptyAction); - } - if (setupPlugins.usageCollection) { initStatsReporter(setupPlugins.usageCollection.reportUiStats); } @@ -158,12 +134,6 @@ export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDe resetInterpreter(); destroyStore(); - startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, emptyAction.id); - if (restoreAction) { - startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, restoreAction); - restoreAction = undefined; - } - coreStart.chrome.setBadge(undefined); coreStart.chrome.setHelpExtension(undefined); diff --git a/x-pack/plugins/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index 586c2b0c2a2597..283196373fe9f7 100644 --- a/x-pack/plugins/case/common/api/cases/case.ts +++ b/x-pack/plugins/case/common/api/cases/case.ts @@ -11,6 +11,7 @@ import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; import { CasesStatusResponseRt } from './status'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { ActionTypeExecutorResult } from '../../../../actions/server/types'; const StatusRt = rt.union([rt.literal('open'), rt.literal('closed')]); diff --git a/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts b/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts index 846013674986e6..4aa6725159043d 100644 --- a/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts +++ b/x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts @@ -27,7 +27,7 @@ export const getActions = (): FindActionResult[] => [ referencedByCount: 0, }, { - id: 'd611af27-3532-4da9-8034-271fee81d634', + id: '123', actionTypeId: '.servicenow', name: 'ServiceNow', config: { diff --git a/x-pack/plugins/case/server/routes/api/cases/push_case.ts b/x-pack/plugins/case/server/routes/api/cases/push_case.ts index 5be36d34549b7a..871e78495c5ddb 100644 --- a/x-pack/plugins/case/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/push_case.ts @@ -37,15 +37,23 @@ export function initPushCaseUserActionApi({ async (context, request, response) => { try { const client = context.core.savedObjects.client; + const actionsClient = await context.actions?.getActionsClient(); + const caseId = request.params.case_id; const query = pipe( CaseExternalServiceRequestRt.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); + + if (actionsClient == null) { + throw Boom.notFound('Action client have not been found'); + } + const { username, full_name, email } = await caseService.getUser({ request, response }); + const pushedDate = new Date().toISOString(); - const [myCase, myCaseConfigure, totalCommentsFindByCases] = await Promise.all([ + const [myCase, myCaseConfigure, totalCommentsFindByCases, connectors] = await Promise.all([ caseService.getCase({ client, caseId: request.params.case_id, @@ -60,6 +68,7 @@ export function initPushCaseUserActionApi({ perPage: 1, }, }), + actionsClient.getAll(), ]); if (myCase.attributes.status === 'closed') { @@ -85,9 +94,15 @@ export function initPushCaseUserActionApi({ }; const caseConfigureConnectorId = getConnectorId(myCaseConfigure); + // old case may not have new attribute connector_id, so we default to the configured system - const updateConnectorId = - myCase.attributes.connector_id == null ? { connector_id: caseConfigureConnectorId } : {}; + const updateConnectorId = { + connector_id: myCase.attributes.connector_id ?? caseConfigureConnectorId, + }; + + if (!connectors.some(connector => connector.id === updateConnectorId.connector_id)) { + throw Boom.notFound('Connector not found or set to none'); + } const [updatedCase, updatedComments] = await Promise.all([ caseService.patchCase({ diff --git a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts index 797141b0996af4..96884cf4bead88 100644 --- a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts +++ b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts @@ -24,8 +24,8 @@ export const APPS = { }; export const MANAGEMENT_ID = 'cross_cluster_replication'; -export const BASE_PATH = `/management/elasticsearch/${MANAGEMENT_ID}`; -export const BASE_PATH_REMOTE_CLUSTERS = '/management/elasticsearch/remote_clusters'; +export const BASE_PATH = `/management/data/${MANAGEMENT_ID}`; +export const BASE_PATH_REMOTE_CLUSTERS = '/management/data/remote_clusters'; export const API_BASE_PATH = '/api/cross_cluster_replication'; export const API_REMOTE_CLUSTERS_BASE_PATH = '/api/remote_clusters'; export const API_INDEX_MANAGEMENT_BASE_PATH = '/api/index_management'; diff --git a/x-pack/plugins/cross_cluster_replication/public/plugin.ts b/x-pack/plugins/cross_cluster_replication/public/plugin.ts index dfe9e4e657c30b..561da838a42020 100644 --- a/x-pack/plugins/cross_cluster_replication/public/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/public/plugin.ts @@ -9,6 +9,7 @@ import { get } from 'lodash'; import { first } from 'rxjs/operators'; import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN, MANAGEMENT_ID } from '../common/constants'; import { init as initUiMetric } from './app/services/track_ui_metric'; import { init as initNotification } from './app/services/notifications'; @@ -22,7 +23,7 @@ export class CrossClusterReplicationPlugin implements Plugin { public setup(coreSetup: CoreSetup, plugins: PluginDependencies) { const { licensing, remoteClusters, usageCollection, management, indexManagement } = plugins; - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.Data); const { http, @@ -36,7 +37,7 @@ export class CrossClusterReplicationPlugin implements Plugin { initUiMetric(usageCollection); initNotification(toasts, fatalErrors); - const ccrApp = esSection!.registerApp({ + const ccrApp = esSection.registerApp({ id: MANAGEMENT_ID, title: PLUGIN.TITLE, order: 6, diff --git a/x-pack/plugins/cross_cluster_replication/server/plugin.ts b/x-pack/plugins/cross_cluster_replication/server/plugin.ts index 25c99803480f3c..7ef085a21ac1a4 100644 --- a/x-pack/plugins/cross_cluster_replication/server/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/server/plugin.ts @@ -15,6 +15,7 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, Logger, PluginInitializerContext, @@ -36,6 +37,13 @@ interface CrossClusterReplicationContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + // Extend the elasticsearchJs client with additional endpoints. + const esClientConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('crossClusterReplication', esClientConfig); +} + const ccrDataEnricher = async (indicesList: Index[], callWithRequest: APICaller) => { if (!indicesList?.length) { return indicesList; @@ -69,6 +77,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin; private readonly license: License; private readonly logger: Logger; + private ccrEsClient?: ICustomClusterClient; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -77,7 +86,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin { + http.registerRouteHandlerContext('crossClusterReplication', async (ctx, request) => { + this.ccrEsClient = this.ccrEsClient ?? (await getCustomEsClient(getStartServices)); return { - client: ccrEsClient.asScoped(request), + client: this.ccrEsClient.asScoped(request), }; }); @@ -135,5 +142,10 @@ export class CrossClusterReplicationServerPlugin implements Plugin -1; } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx index 1ead7a38d4c9b9..c94d19d28e6da5 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx @@ -134,10 +134,12 @@ describe('.execute() & getHref', () => { }; const context = ({ - data: useRangeEvent - ? ({ range: {} } as RangeSelectTriggerContext['data']) - : ({ data: [] } as ValueClickTriggerContext['data']), - timeFieldName: 'order_date', + data: { + ...(useRangeEvent + ? ({ range: {} } as RangeSelectTriggerContext['data']) + : ({ data: [] } as ValueClickTriggerContext['data'])), + timeFieldName: 'order_date', + }, embeddable: { getInput: () => ({ filters: [], diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx index d33dd1ef64e0d5..21afa6e822dc50 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx @@ -127,9 +127,9 @@ export class DashboardToDashboardDrilldown } })(); - if (context.timeFieldName) { + if (context.data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - context.timeFieldName, + context.data.timeFieldName, filtersFromEvent ); filtersFromEvent = restOfFilters; diff --git a/x-pack/plugins/endpoint/server/plugin.test.ts b/x-pack/plugins/endpoint/server/plugin.test.ts index 45e9591a149750..215b26942bcdb5 100644 --- a/x-pack/plugins/endpoint/server/plugin.test.ts +++ b/x-pack/plugins/endpoint/server/plugin.test.ts @@ -34,7 +34,6 @@ describe('test endpoint plugin', () => { registerFeature: jest.fn(), getFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), - registerLegacyAPI: jest.fn(), }; }); diff --git a/x-pack/plugins/features/kibana.json b/x-pack/plugins/features/kibana.json index 6e51f3b6507106..1cab1821b1bf59 100644 --- a/x-pack/plugins/features/kibana.json +++ b/x-pack/plugins/features/kibana.json @@ -2,6 +2,7 @@ "id": "features", "version": "8.0.0", "kibanaVersion": "kibana", + "requiredPlugins": ["licensing"], "optionalPlugins": ["visTypeTimelion"], "configPath": ["xpack", "features"], "server": true, diff --git a/x-pack/plugins/features/server/mocks.ts b/x-pack/plugins/features/server/mocks.ts index ebaa5f1a504ca9..d9437169a7453b 100644 --- a/x-pack/plugins/features/server/mocks.ts +++ b/x-pack/plugins/features/server/mocks.ts @@ -11,7 +11,6 @@ const createSetup = (): jest.Mocked => { getFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), registerFeature: jest.fn(), - registerLegacyAPI: jest.fn(), }; }; diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts new file mode 100644 index 00000000000000..3d7cf19e58b0ea --- /dev/null +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { coreMock, savedObjectsServiceMock } from 'src/core/server/mocks'; + +import { Plugin } from './plugin'; +const initContext = coreMock.createPluginInitializerContext(); +const coreSetup = coreMock.createSetup(); +const coreStart = coreMock.createStart(); +const typeRegistry = savedObjectsServiceMock.createTypeRegistryMock(); +typeRegistry.getAllTypes.mockReturnValue([ + { + name: 'foo', + hidden: false, + mappings: { properties: {} }, + namespaceType: 'single' as 'single', + }, + { + name: 'bar', + hidden: true, + mappings: { properties: {} }, + namespaceType: 'agnostic' as 'agnostic', + }, +]); +coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry); + +describe('Features Plugin', () => { + it('returns OSS + registered features', async () => { + const plugin = new Plugin(initContext); + const { registerFeature } = await plugin.setup(coreSetup, {}); + registerFeature({ + id: 'baz', + name: 'baz', + app: [], + privileges: null, + }); + + const { getFeatures } = await plugin.start(coreStart); + + expect(getFeatures().map(f => f.id)).toMatchInlineSnapshot(` + Array [ + "baz", + "discover", + "visualize", + "dashboard", + "dev_tools", + "advancedSettings", + "indexPatterns", + "savedObjectsManagement", + ] + `); + }); + + it('returns OSS + registered features with timelion when available', async () => { + const plugin = new Plugin(initContext); + const { registerFeature } = await plugin.setup(coreSetup, { + visTypeTimelion: { uiEnabled: true }, + }); + registerFeature({ + id: 'baz', + name: 'baz', + app: [], + privileges: null, + }); + + const { getFeatures } = await plugin.start(coreStart); + + expect(getFeatures().map(f => f.id)).toMatchInlineSnapshot(` + Array [ + "baz", + "discover", + "visualize", + "dashboard", + "dev_tools", + "advancedSettings", + "indexPatterns", + "savedObjectsManagement", + "timelion", + ] + `); + }); + + it('registers not hidden saved objects types', async () => { + const plugin = new Plugin(initContext); + await plugin.setup(coreSetup, {}); + const { getFeatures } = await plugin.start(coreStart); + + const soTypes = + getFeatures().find(f => f.id === 'savedObjectsManagement')?.privileges?.all.savedObject.all || + []; + + expect(soTypes.includes('foo')).toBe(true); + expect(soTypes.includes('bar')).toBe(false); + }); +}); diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 2405f05768a2f0..e3480eda9fe7db 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -6,13 +6,14 @@ import { CoreSetup, + CoreStart, + SavedObjectsServiceStart, Logger, PluginInitializerContext, RecursiveReadonly, } from '../../../../src/core/server'; import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { deepFreeze } from '../../../../src/core/server'; -import { XPackInfo } from '../../../legacy/plugins/xpack_main/server/lib/xpack_info'; import { PluginSetupContract as TimelionSetupContract } from '../../../../src/plugins/vis_type_timelion/server'; import { FeatureRegistry } from './feature_registry'; import { Feature, FeatureConfig } from '../common/feature'; @@ -25,39 +26,26 @@ import { defineRoutes } from './routes'; */ export interface PluginSetupContract { registerFeature(feature: FeatureConfig): void; + /* + * Calling this function during setup will crash Kibana. + * Use start contract instead. + * @deprecated + * */ getFeatures(): Feature[]; getFeaturesUICapabilities(): UICapabilities; - registerLegacyAPI: (legacyAPI: LegacyAPI) => void; } export interface PluginStartContract { getFeatures(): Feature[]; } -/** - * Describes a set of APIs that are available in the legacy platform only and required by this plugin - * to function properly. - */ -export interface LegacyAPI { - xpackInfo: Pick; - savedObjectTypes: string[]; -} - /** * Represents Features Plugin instance that will be managed by the Kibana plugin system. */ export class Plugin { private readonly logger: Logger; - private readonly featureRegistry: FeatureRegistry = new FeatureRegistry(); - - private legacyAPI?: LegacyAPI; - private readonly getLegacyAPI = () => { - if (!this.legacyAPI) { - throw new Error('Legacy API is not registered!'); - } - return this.legacyAPI; - }; + private isTimelionEnabled: boolean = false; constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); @@ -67,39 +55,49 @@ export class Plugin { core: CoreSetup, { visTypeTimelion }: { visTypeTimelion?: TimelionSetupContract } ): Promise> { + this.isTimelionEnabled = visTypeTimelion !== undefined && visTypeTimelion.uiEnabled; + defineRoutes({ router: core.http.createRouter(), featureRegistry: this.featureRegistry, - getLegacyAPI: this.getLegacyAPI, }); return deepFreeze({ registerFeature: this.featureRegistry.register.bind(this.featureRegistry), getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), getFeaturesUICapabilities: () => uiCapabilitiesForFeatures(this.featureRegistry.getAll()), - - registerLegacyAPI: (legacyAPI: LegacyAPI) => { - this.legacyAPI = legacyAPI; - - // Register OSS features. - for (const feature of buildOSSFeatures({ - savedObjectTypes: this.legacyAPI.savedObjectTypes, - includeTimelion: visTypeTimelion !== undefined && visTypeTimelion.uiEnabled, - })) { - this.featureRegistry.register(feature); - } - }, }); } - public start(): RecursiveReadonly { - this.logger.debug('Starting plugin'); + public start(core: CoreStart): RecursiveReadonly { + this.registerOssFeatures(core.savedObjects); + return deepFreeze({ getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), }); } - public stop() { - this.logger.debug('Stopping plugin'); + public stop() {} + + private registerOssFeatures(savedObjects: SavedObjectsServiceStart) { + const registry = savedObjects.getTypeRegistry(); + const savedObjectTypes = registry + .getAllTypes() + .filter(t => !t.hidden) + .map(t => t.name); + + this.logger.debug( + `Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${ + this.isTimelionEnabled + }.` + ); + const features = buildOSSFeatures({ + savedObjectTypes, + includeTimelion: this.isTimelionEnabled, + }); + + for (const feature of features) { + this.featureRegistry.register(feature); + } } } diff --git a/x-pack/plugins/features/server/routes/index.test.ts b/x-pack/plugins/features/server/routes/index.test.ts index c43e2a5195fe72..67b28b27f931fb 100644 --- a/x-pack/plugins/features/server/routes/index.test.ts +++ b/x-pack/plugins/features/server/routes/index.test.ts @@ -7,12 +7,20 @@ import { FeatureRegistry } from '../feature_registry'; import { defineRoutes } from './index'; -import { httpServerMock, httpServiceMock } from '../../../../../src/core/server/mocks'; -import { XPackInfoLicense } from '../../../../legacy/plugins/xpack_main/server/lib/xpack_info_license'; +import { httpServerMock, httpServiceMock, coreMock } from '../../../../../src/core/server/mocks'; +import { LicenseType } from '../../../licensing/server/'; +import { licensingMock } from '../../../licensing/server/mocks'; import { RequestHandler } from '../../../../../src/core/server'; import { FeatureConfig } from '../../common'; -let currentLicenseLevel: string = 'gold'; +function createContextMock(licenseType: LicenseType = 'gold') { + return { + core: coreMock.createRequestHandlerContext(), + licensing: { + license: licensingMock.createLicense({ license: { type: licenseType } }), + }, + }; +} describe('GET /api/features', () => { let routeHandler: RequestHandler; @@ -53,16 +61,6 @@ describe('GET /api/features', () => { defineRoutes({ router: routerMock, featureRegistry, - getLegacyAPI: () => ({ - xpackInfo: { - license: { - isOneOf(candidateLicenses: string[]) { - return candidateLicenses.includes(currentLicenseLevel); - }, - } as XPackInfoLicense, - }, - savedObjectTypes: [], - }), }); routeHandler = routerMock.get.mock.calls[0][1]; @@ -70,7 +68,7 @@ describe('GET /api/features', () => { it('returns a list of available features, sorted by their configured order', async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: {} } as any, mockResponse); + routeHandler(createContextMock(), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -98,10 +96,8 @@ describe('GET /api/features', () => { }); it(`by default does not return features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: {} } as any, mockResponse); + routeHandler(createContextMock('basic'), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -126,10 +122,12 @@ describe('GET /api/features', () => { }); it(`ignoreValidLicenses=false does not return features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: { ignoreValidLicenses: false } } as any, mockResponse); + routeHandler( + createContextMock('basic'), + { query: { ignoreValidLicenses: false } } as any, + mockResponse + ); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -154,10 +152,12 @@ describe('GET /api/features', () => { }); it(`ignoreValidLicenses=true returns features that arent allowed by current license`, async () => { - currentLicenseLevel = 'basic'; - const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(undefined as any, { query: { ignoreValidLicenses: true } } as any, mockResponse); + routeHandler( + createContextMock('basic'), + { query: { ignoreValidLicenses: true } } as any, + mockResponse + ); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index 428500c3daa88d..d07b4886930910 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -6,7 +6,6 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '../../../../../src/core/server'; -import { LegacyAPI } from '../plugin'; import { FeatureRegistry } from '../feature_registry'; /** @@ -15,10 +14,9 @@ import { FeatureRegistry } from '../feature_registry'; export interface RouteDefinitionParams { router: IRouter; featureRegistry: FeatureRegistry; - getLegacyAPI: () => LegacyAPI; } -export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDefinitionParams) { +export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) { router.get( { path: '/api/features', @@ -37,7 +35,8 @@ export function defineRoutes({ router, featureRegistry, getLegacyAPI }: RouteDef request.query.ignoreValidLicenses || !feature.validLicenses || !feature.validLicenses.length || - getLegacyAPI().xpackInfo.license.isOneOf(feature.validLicenses) + (context.licensing!.license.type && + feature.validLicenses.includes(context.licensing!.license.type)) ) .sort( (f1, f2) => diff --git a/x-pack/plugins/graph/common/constants.ts b/x-pack/plugins/graph/common/constants.ts index 3c3ee2b1258964..42c05cfde1a44b 100644 --- a/x-pack/plugins/graph/common/constants.ts +++ b/x-pack/plugins/graph/common/constants.ts @@ -7,5 +7,5 @@ export const APP_ICON = 'graphApp'; export function createWorkspacePath(id: string) { - return `/app/graph/#/workspace/${id}`; + return `/app/graph#/workspace/${id}`; } diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 96e7ea3ff22327..4cae14f8939b20 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["licensing", "data", "navigation"], + "requiredPlugins": ["licensing", "data", "navigation", "savedObjects"], "optionalPlugins": ["home", "features"], "configPath": ["xpack", "graph"] } diff --git a/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html b/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html index 2f75d663126b26..4e0c13442d2674 100644 --- a/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html +++ b/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html @@ -7,5 +7,6 @@ listing-limit="listingLimit" capabilities="capabilities" initial-filter="initialFilter" + initialPageSize="initialPageSize" core-start="coreStart" > diff --git a/x-pack/plugins/graph/public/app.js b/x-pack/plugins/graph/public/app.js index 7effe44375b1f7..d4199fbd092b43 100644 --- a/x-pack/plugins/graph/public/app.js +++ b/x-pack/plugins/graph/public/app.js @@ -46,13 +46,13 @@ export function initGraphApp(angularModule, deps) { addBasePath, getBasePath, data, - config, capabilities, coreStart, storage, canEditDrillDownUrls, graphSavePolicy, overlays, + savedObjects, } = deps; const app = angularModule; @@ -77,6 +77,7 @@ export function initGraphApp(angularModule, deps) { ['hideWriteControls', { watchDepth: 'reference' }], ['capabilities', { watchDepth: 'reference' }], ['initialFilter', { watchDepth: 'reference' }], + ['initialPageSize', { watchDepth: 'reference' }], ]); }); @@ -111,7 +112,8 @@ export function initGraphApp(angularModule, deps) { template: listingTemplate, badge: getReadonlyBadge, controller: function($location, $scope) { - $scope.listingLimit = config.get('savedObjects:listingLimit'); + $scope.listingLimit = savedObjects.settings.getListingLimit(); + $scope.initialPageSize = savedObjects.settings.getPerPage(); $scope.create = () => { $location.url(getNewPath()); }; diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index 4aff9d28f37715..7c0fb867b9ada4 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -22,7 +22,6 @@ import { PluginInitializerContext, SavedObjectsClientContract, ToastsStart, - IUiSettingsClient, OverlayStart, } from 'kibana/public'; // @ts-ignore @@ -39,6 +38,7 @@ import { } from '../../../../src/plugins/kibana_legacy/public'; import './index.scss'; +import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; /** * These are dependencies of the Graph app besides the base dependencies @@ -56,7 +56,6 @@ export interface GraphDependencies { navigation: NavigationStart; licensing: LicensingPluginSetup; chrome: ChromeStart; - config: IUiSettingsClient; toastNotifications: ToastsStart; indexPatterns: IndexPatternsContract; data: ReturnType; @@ -67,6 +66,7 @@ export interface GraphDependencies { canEditDrillDownUrls: boolean; graphSavePolicy: string; overlays: OverlayStart; + savedObjects: SavedObjectsStart; } export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { diff --git a/x-pack/plugins/graph/public/components/listing.tsx b/x-pack/plugins/graph/public/components/listing.tsx index 37f27523cbad24..b89ee2489d7f3f 100644 --- a/x-pack/plugins/graph/public/components/listing.tsx +++ b/x-pack/plugins/graph/public/components/listing.tsx @@ -24,6 +24,7 @@ export interface ListingProps { hideWriteControls: boolean; capabilities: { save: boolean; delete: boolean }; initialFilter: string; + initialPageSize: number; } export function Listing(props: ListingProps) { @@ -38,6 +39,7 @@ export function Listing(props: ListingProps) { tableColumns={getTableColumns(props.getViewUrl)} listingLimit={props.listingLimit} initialFilter={props.initialFilter} + initialPageSize={props.initialPageSize} noItemsFragment={getNoItemsMessage( props.capabilities.save === false, props.createItem, @@ -53,7 +55,6 @@ export function Listing(props: ListingProps) { tableListTitle={i18n.translate('xpack.graph.listing.graphsTitle', { defaultMessage: 'Graphs', })} - uiSettings={props.coreStart.uiSettings} /> ); diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index b8691190625387..9d6d083b3e4b9f 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -23,6 +23,7 @@ import { } from '../../../../src/plugins/home/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { ConfigSchema } from '../config'; +import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; export interface GraphPluginSetupDependencies { licensing: LicensingPluginSetup; @@ -32,6 +33,7 @@ export interface GraphPluginSetupDependencies { export interface GraphPluginStartDependencies { navigation: NavigationStart; data: DataPublicPluginStart; + savedObjects: SavedObjectsStart; } export class GraphPlugin @@ -92,10 +94,10 @@ export class GraphPlugin capabilities: coreStart.application.capabilities.graph, coreStart, chrome: coreStart.chrome, - config: coreStart.uiSettings, toastNotifications: coreStart.notifications.toasts, indexPatterns: pluginsStart.data!.indexPatterns, overlays: coreStart.overlays, + savedObjects: pluginsStart.savedObjects, }); }, }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap index dbdbe2b52bd563..d64c8c6239fcd3 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap @@ -364,11 +364,11 @@ exports[`extend index management ilm summary extension should return extension w className="euiDescriptionList__description" > testy @@ -748,11 +748,11 @@ exports[`extend index management ilm summary extension should return extension w className="euiDescriptionList__description" > testy diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap b/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap index cbc735bb150f54..857a63826505e0 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/__snapshots__/policy_table.test.js.snap @@ -94,7 +94,7 @@ exports[`policy table should show empty state when there are not any policies 1` { const [coreStart] = await getStartServices(); const { diff --git a/x-pack/plugins/index_management/common/constants/base_path.ts b/x-pack/plugins/index_management/common/constants/base_path.ts index 3a49e2870b6094..c17d96250b1fb7 100644 --- a/x-pack/plugins/index_management/common/constants/base_path.ts +++ b/x-pack/plugins/index_management/common/constants/base_path.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const BASE_PATH = '/management/elasticsearch/index_management/'; +export const BASE_PATH = '/management/data/index_management/'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js index 5d0e2fd4530ef2..f6250a7a6e25c7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js @@ -11,7 +11,7 @@ import { getPager, getFilter, isDetailPanelOpen, - showSystemIndices, + showHiddenIndices, getSortField, isSortAscending, getIndicesAsArray, @@ -26,7 +26,7 @@ import { pageChanged, pageSizeChanged, sortChanged, - showSystemIndicesChanged, + showHiddenIndicesChanged, loadIndices, reloadIndices, toggleChanged, @@ -42,7 +42,7 @@ const mapStateToProps = state => { indices: getPageOfIndices(state), pager: getPager(state), filter: getFilter(state), - showSystemIndices: showSystemIndices(state), + showHiddenIndices: showHiddenIndices(state), sortField: getSortField(state), isSortAscending: isSortAscending(state), indicesLoading: indicesLoading(state), @@ -65,8 +65,8 @@ const mapDispatchToProps = dispatch => { sortChanged: (sortField, isSortAscending) => { dispatch(sortChanged({ sortField, isSortAscending })); }, - showSystemIndicesChanged: showSystemIndices => { - dispatch(showSystemIndicesChanged({ showSystemIndices })); + showHiddenIndicesChanged: showHiddenIndices => { + dispatch(showHiddenIndicesChanged({ showHiddenIndices })); }, toggleChanged: (toggleName, toggleValue) => { dispatch(toggleChanged({ toggleName, toggleValue })); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index b659e2ec86a9b6..7a0a6269a6ab86 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -400,8 +400,8 @@ export class IndexTable extends Component { render() { const { filter, - showSystemIndices, - showSystemIndicesChanged, + showHiddenIndices, + showHiddenIndicesChanged, indices, loadIndices, indicesLoading, @@ -459,13 +459,13 @@ export class IndexTable extends Component { })} showSystemIndicesChanged(event.target.checked)} + id="checkboxShowHiddenIndices" + checked={showHiddenIndices} + onChange={event => showHiddenIndicesChanged(event.target.checked)} label={ } /> diff --git a/x-pack/plugins/index_management/public/application/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts index e56e8d474d7806..8bd9fc24322328 100644 --- a/x-pack/plugins/index_management/public/application/services/navigation.ts +++ b/x-pack/plugins/index_management/public/application/services/navigation.ts @@ -19,8 +19,6 @@ export const getIndexListUri = (filter: any) => { export const getILMPolicyPath = (policyName: string) => { return encodeURI( - `#/management/elasticsearch/index_lifecycle_management/policies/edit/${encodeURIComponent( - policyName - )}` + `#/management/data/index_lifecycle_management/policies/edit/${encodeURIComponent(policyName)}` ); }; diff --git a/x-pack/plugins/index_management/public/application/store/actions/table_state.js b/x-pack/plugins/index_management/public/application/store/actions/table_state.js index cc6c66aab4f1a0..70e0de74d02788 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/table_state.js +++ b/x-pack/plugins/index_management/public/application/store/actions/table_state.js @@ -17,8 +17,8 @@ export const pageSizeChanged = createAction('INDEX_MANAGEMENT_PAGE_SIZE_CHANGED' export const sortChanged = createAction('INDEX_MANAGEMENT_SORT_CHANGED'); -export const showSystemIndicesChanged = createAction( - 'INDEX_MANAGEMENT_SHOW_SYSTEM_INDICES_CHANGED' +export const showHiddenIndicesChanged = createAction( + 'INDEX_MANAGEMENT_SHOW_HIDDEN_INDICES_CHANGED' ); export const toggleChanged = createAction('INDEX_MANAGEMENT_TOGGLE_CHANGED'); diff --git a/x-pack/plugins/index_management/public/application/store/reducers/table_state.js b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js index 8bb4e77619ef6e..e90fa72aa62fe2 100644 --- a/x-pack/plugins/index_management/public/application/store/reducers/table_state.js +++ b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js @@ -10,7 +10,7 @@ import { pageChanged, pageSizeChanged, sortChanged, - showSystemIndicesChanged, + showHiddenIndicesChanged, toggleChanged, } from '../actions'; @@ -20,7 +20,7 @@ export const defaultTableState = { currentPage: 0, sortField: 'index.name', isSortAscending: true, - showSystemIndices: false, + showHiddenIndices: false, }; export const tableState = handleActions( @@ -33,12 +33,12 @@ export const tableState = handleActions( currentPage: 0, }; }, - [showSystemIndicesChanged](state, action) { - const { showSystemIndices } = action.payload; + [showHiddenIndicesChanged](state, action) { + const { showHiddenIndices } = action.payload; return { ...state, - showSystemIndices, + showHiddenIndices, }; }, [toggleChanged](state, action) { diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js index 3ddbf28651fa07..0f481c55782b00 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.js @@ -86,9 +86,9 @@ const getFilteredIndices = createSelector( (indices, allIds, tableState) => { let indexArray = allIds.map(indexName => indices[indexName]); indexArray = filterByToggles(indexArray, tableState.toggleNameToVisibleMap); - const systemFilteredIndexes = tableState.showSystemIndices + const systemFilteredIndexes = tableState.showHiddenIndices ? indexArray - : indexArray.filter(index => !(index.name + '').startsWith('.')); + : indexArray.filter(index => !(index.name + '').startsWith('.') && !index.hidden); const filter = tableState.filter || EuiSearchBar.Query.MATCH_ALL; return EuiSearchBar.Query.execute(filter, systemFilteredIndexes, { defaultFields: defaultFilterFields, @@ -151,9 +151,9 @@ export const getCurrentPage = createSelector(getPager, pager => { export const getFilter = createSelector(getTableState, ({ filter }) => filter); -export const showSystemIndices = createSelector( +export const showHiddenIndices = createSelector( getTableState, - ({ showSystemIndices }) => showSystemIndices + ({ showHiddenIndices }) => showHiddenIndices ); export const isSortAscending = createSelector( diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 78e80687abeb46..5fb8ce72077299 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup } from '../../../../src/core/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { UIM_APP_NAME, PLUGIN } from '../common/constants'; import { httpService } from './application/services/http'; @@ -48,10 +48,10 @@ export class IndexMgmtUIPlugin { notificationService.setup(notifications); this.uiMetricService.setup(usageCollection); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Data).registerApp({ id: PLUGIN.id, title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), - order: 2, + order: 0, mount: async params => { const { mountManagementSection } = await import('./application/mount_management_section'); const services = { diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index cd2964df23d9bf..89423672615113 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -10,7 +10,7 @@ export { UseRequestConfig, sendRequest, useRequest, -} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +} from '../../../../src/plugins/es_ui_shared/public/'; export { FormSchema, diff --git a/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts deleted file mode 100644 index d5ba11df1f0a99..00000000000000 --- a/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import sinon from 'sinon'; -import { fetchAliases } from './fetch_aliases'; - -describe('fetching aliases', () => { - const fetchFn = fetchAliases; - - test('should return map of aliases for indices', async () => { - const retVal = [ - { index: 'test1Index', alias: 'test1Alias' }, - { index: 'test2Index', alias: 'test1Alias' }, - { index: 'test3Index', alias: 'test2Alias' }, - { index: 'test3Index', alias: 'test3Alias' }, - ]; - const mockCallWithRequest = sinon.spy(() => { - return retVal; - }); - - const results = await fetchFn(mockCallWithRequest); - - expect(mockCallWithRequest.called); - expect(results).toBeDefined(); - expect(results).toMatchObject({ - test1Index: ['test1Alias'], - test2Index: ['test1Alias'], - test3Index: ['test2Alias', 'test3Alias'], - }); - }); - - test('should return an empty object if no aliases exist', async () => { - const mockCallWithRequest = sinon.spy(() => { - return []; - }); - - const results = await fetchFn(mockCallWithRequest); - - expect(mockCallWithRequest.called); - expect(results).toBeDefined(); - expect(results).toMatchObject({}); - }); -}); diff --git a/x-pack/plugins/index_management/server/lib/fetch_aliases.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.ts deleted file mode 100644 index 794a260fb9c518..00000000000000 --- a/x-pack/plugins/index_management/server/lib/fetch_aliases.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export async function fetchAliases(callWithRequest: any) { - const results = await callWithRequest('cat.aliases', { format: 'json' }); - return results.reduce( - (hash: { [key: string]: any }, { index, alias }: { index: string; alias: string }) => { - (hash[index] = hash[index] || []).push(alias); - return hash; - }, - {} - ); -} diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts index d9f01ee060145c..1f62680a41cbcb 100644 --- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts +++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts @@ -3,9 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { CatIndicesParams } from 'elasticsearch'; import { IndexDataEnricher } from '../services'; import { Index, CallAsCurrentUser } from '../types'; -import { fetchAliases } from './fetch_aliases'; interface Hit { health: string; @@ -17,20 +17,64 @@ interface Hit { 'docs.count': any; 'store.size': any; sth: 'true' | 'false'; + hidden: boolean; } -interface Aliases { - [key: string]: string[]; +interface IndexInfo { + aliases: { [aliasName: string]: unknown }; + mappings: unknown; + settings: { + index: { + hidden: 'true' | 'false'; + }; + }; } -interface Params { - format: string; - h: string; - index?: string[]; +interface GetIndicesResponse { + [indexName: string]: IndexInfo; } -function formatHits(hits: Hit[], aliases: Aliases): Index[] { - return hits.map((hit: Hit) => { +async function fetchIndicesCall( + callAsCurrentUser: CallAsCurrentUser, + indexNames?: string[] +): Promise { + const indexNamesString = indexNames && indexNames.length ? indexNames.join(',') : '*'; + + // This call retrieves alias and settings (incl. hidden status) information about indices + const indices: GetIndicesResponse = await callAsCurrentUser('transport.request', { + method: 'GET', + path: `/${indexNamesString}`, + query: { + expand_wildcards: 'hidden,all', + }, + }); + + if (!Object.keys(indices).length) { + return []; + } + + const catQuery: Pick & { + expand_wildcards: string; + index?: string; + } = { + format: 'json', + h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size', + expand_wildcards: 'hidden,all', + index: indexNamesString, + }; + + // This call retrieves health and other high-level information about indices. + const catHits: Hit[] = await callAsCurrentUser('transport.request', { + method: 'GET', + path: '/_cat/indices', + query: catQuery, + }); + + // The two responses should be equal in the number of indices returned + return catHits.map(hit => { + const index = indices[hit.index]; + const aliases = Object.keys(index.aliases); + return { health: hit.health, status: hit.status, @@ -41,32 +85,17 @@ function formatHits(hits: Hit[], aliases: Aliases): Index[] { documents: hit['docs.count'], size: hit['store.size'], isFrozen: hit.sth === 'true', // sth value coming back as a string from ES - aliases: aliases.hasOwnProperty(hit.index) ? aliases[hit.index] : 'none', + aliases: aliases.length ? aliases : 'none', + hidden: index.settings.index.hidden === 'true', }; }); } -async function fetchIndicesCall(callAsCurrentUser: CallAsCurrentUser, indexNames?: string[]) { - const params: Params = { - format: 'json', - h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size', - }; - - if (indexNames) { - params.index = indexNames; - } - - return await callAsCurrentUser('cat.indices', params); -} - export const fetchIndices = async ( callAsCurrentUser: CallAsCurrentUser, indexDataEnricher: IndexDataEnricher, indexNames?: string[] ) => { - const aliases = await fetchAliases(callAsCurrentUser); - const hits = await fetchIndicesCall(callAsCurrentUser, indexNames); - const indices = formatHits(hits, aliases); - + const indices = await fetchIndicesCall(callAsCurrentUser, indexNames); return await indexDataEnricher.enrichIndices(indices, callAsCurrentUser); }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx index 8bcf0e9ed5be5d..bafb38459b17b3 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx @@ -35,7 +35,7 @@ export const MetricsAlertDropdown = () => { icon="tableOfContents" key="manageLink" href={kibana.services?.application?.getUrlForApp( - 'kibana#/management/kibana/triggersActions/alerts' + 'kibana#/management/insightsAndAlerting/triggersActions/alerts' )} > diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx index d2904206875c79..a3cebcf33f3864 100644 --- a/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx @@ -35,7 +35,7 @@ export const InventoryAlertDropdown = () => { icon="tableOfContents" key="manageLink" href={kibana.services?.application?.getUrlForApp( - 'kibana#/management/kibana/triggersActions/alerts' + 'kibana#/management/insightsAndAlerting/triggersActions/alerts' )} > diff --git a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx index dd888639b6d073..d808b4f3b64aa2 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx @@ -16,7 +16,7 @@ export const AlertDropdown = () => { const manageAlertsLinkProps = useLinkProps( { app: 'kibana', - hash: 'management/kibana/triggersActions/alerts', + hash: 'management/insightsAndAlerting/triggersActions/alerts', }, { hrefOnly: true, diff --git a/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts b/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts index aad54bd2222b77..94e2537a67a2ac 100644 --- a/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts +++ b/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts @@ -8,8 +8,8 @@ import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import createContainer from 'constate'; -import { HttpHandler } from 'target/types/core/public/http'; -import { ToastInput } from 'target/types/core/public/notifications/toasts/toasts_api'; +import { HttpHandler } from 'src/core/public'; +import { ToastInput } from 'src/core/public'; import { SourceResponseRuntimeType, SourceResponse, diff --git a/x-pack/plugins/infra/public/hooks/use_http_request.tsx b/x-pack/plugins/infra/public/hooks/use_http_request.tsx index e00abe6380498f..0a0c876bb63ce6 100644 --- a/x-pack/plugins/infra/public/hooks/use_http_request.tsx +++ b/x-pack/plugins/infra/public/hooks/use_http_request.tsx @@ -7,8 +7,8 @@ import React, { useMemo, useState } from 'react'; import { IHttpFetchError } from 'src/core/public'; import { i18n } from '@kbn/i18n'; -import { HttpHandler } from 'target/types/core/public/http'; -import { ToastInput } from 'target/types/core/public/notifications/toasts/toasts_api'; +import { HttpHandler } from 'src/core/public'; +import { ToastInput } from 'src/core/public'; import { useTrackedPromise } from '../utils/use_tracked_promise'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index da6d77ef4b4783..6b4ac8b1ba060f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -7,7 +7,7 @@ import DateMath from '@elastic/datemath'; import { isEqual } from 'lodash'; import { useEffect, useState } from 'react'; -import { HttpHandler } from 'target/types/core/public/http'; +import { HttpHandler } from 'src/core/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { SourceQuery } from '../../../../../common/graphql/types'; import { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts index 64669b5cd123a5..085bad2d183753 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getFlattenedObject } from '../../../../../../../src/core/utils'; +export { getFlattenedObject } from '../../../../../../../src/core/public'; export { agentConfigRouteService, diff --git a/x-pack/plugins/ingest_pipelines/common/constants.ts b/x-pack/plugins/ingest_pipelines/common/constants.ts index edf681c276a842..de291e364e02f4 100644 --- a/x-pack/plugins/ingest_pipelines/common/constants.ts +++ b/x-pack/plugins/ingest_pipelines/common/constants.ts @@ -11,7 +11,7 @@ export const PLUGIN_ID = 'ingest_pipelines'; export const PLUGIN_MIN_LICENSE_TYPE = basicLicense; -export const BASE_PATH = '/management/elasticsearch/ingest_pipelines'; +export const BASE_PATH = '/management/ingest/ingest_pipelines'; export const API_BASE_PATH = '/api/ingest_pipelines'; diff --git a/x-pack/plugins/ingest_pipelines/public/plugin.ts b/x-pack/plugins/ingest_pipelines/public/plugin.ts index 0ab46f386e83b5..d537f8d68d7e4d 100644 --- a/x-pack/plugins/ingest_pipelines/public/plugin.ts +++ b/x-pack/plugins/ingest_pipelines/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin } from 'src/core/public'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN_ID } from '../common/constants'; import { uiMetricService, apiService } from './application/services'; import { Dependencies } from './types'; @@ -20,7 +21,7 @@ export class IngestPipelinesPlugin implements Plugin { uiMetricService.setup(usageCollection); apiService.setup(http, uiMetricService); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Ingest).registerApp({ id: PLUGIN_ID, order: 1, title: i18n.translate('xpack.ingestPipelines.appTitle', { diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index cfa946ff942ecd..a2ee3215260520 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -12,7 +12,7 @@ export { UseRequestConfig, sendRequest, useRequest, -} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +} from '../../../../src/plugins/es_ui_shared/public/'; export { FormSchema, diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index d86a3180f64d9f..a8b22b3e22750f 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -9,10 +9,9 @@ "expressions", "navigation", "kibanaLegacy", - "uiActions", "visualizations", "dashboard" ], - "optionalPlugins": ["embeddable", "usageCollection", "taskManager"], + "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions"], "configPath": ["xpack", "lens"] } diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 6d5b1153ad1bc1..5407389c7fc4c3 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -13,7 +13,7 @@ import { DatatableProps } from './expression'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; import { IAggType } from 'src/plugins/data/public'; -const executeTriggerActions = jest.fn(); +const onClickValue = jest.fn(); function sampleArgs() { const data: LensMultiTable = { @@ -66,7 +66,7 @@ describe('datatable_expression', () => { data={data} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn()} /> ) @@ -87,7 +87,7 @@ describe('datatable_expression', () => { }} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} /> ); @@ -97,18 +97,16 @@ describe('datatable_expression', () => { .first() .simulate('click'); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 0, - row: 0, - table: data.tables.l1, - value: 10110, - }, - ], - negate: true, - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: data.tables.l1, + value: 10110, + }, + ], + negate: true, timeFieldName: undefined, }); }); @@ -127,7 +125,7 @@ describe('datatable_expression', () => { }} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} /> ); @@ -137,18 +135,16 @@ describe('datatable_expression', () => { .at(3) .simulate('click'); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 1, - row: 0, - table: data.tables.l1, - value: 1588024800000, - }, - ], - negate: false, - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 0, + table: data.tables.l1, + value: 1588024800000, + }, + ], + negate: false, timeFieldName: 'b', }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 71d29be1744bb1..3be5c72d2af374 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -10,17 +10,17 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { IAggType } from 'src/plugins/data/public'; -import { FormatFactory, LensMultiTable } from '../types'; +import { + FormatFactory, + ILensInterpreterRenderHandlers, + LensFilterEvent, + LensMultiTable, +} from '../types'; import { ExpressionFunctionDefinition, ExpressionRenderDefinition, - IInterpreterRenderHandlers, } from '../../../../../src/plugins/expressions/public'; import { VisualizationContainer } from '../visualization_container'; -import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { getExecuteTriggerActions } from '../services'; export interface DatatableColumns { columnIds: string[]; } @@ -37,7 +37,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (data: LensFilterEvent['data']) => void; getType: (name: string) => IAggType; }; @@ -125,17 +125,19 @@ export const getDatatableRenderer = (dependencies: { render: async ( domNode: Element, config: DatatableProps, - handlers: IInterpreterRenderHandlers + handlers: ILensInterpreterRenderHandlers ) => { const resolvedFormatFactory = await dependencies.formatFactory; - const executeTriggerActions = getExecuteTriggerActions(); const resolvedGetType = await dependencies.getType; + const onClickValue = (data: LensFilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; ReactDOM.render( , @@ -162,21 +164,19 @@ export function DatatableComponent(props: DatatableRenderProps) { const timeFieldName = negate && isDateHistogram ? undefined : col?.meta?.aggConfigParams?.field; const rowIndex = firstTable.rows.findIndex(row => row[field] === value); - const context: ValueClickTriggerContext = { - data: { - negate, - data: [ - { - row: rowIndex, - column: colIndex, - value, - table: firstTable, - }, - ], - }, + const data: LensFilterEvent['data'] = { + negate, + data: [ + { + row: rowIndex, + column: colIndex, + value, + table: firstTable, + }, + ], timeFieldName, }; - props.executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + props.onClickValue(data); }; return ( diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 44894d31da51d6..5cc3c40591c3f1 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, CoreStart } from 'kibana/public'; +import { CoreSetup } from 'kibana/public'; import { datatableVisualization } from './visualization'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { datatable, datatableColumns, getDatatableRenderer } from './expression'; import { EditorFrameSetup, FormatFactory } from '../types'; -import { setExecuteTriggerActions } from '../services'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; @@ -42,7 +41,4 @@ export class DatatableVisualization { ); editorFrame.registerVisualization(datatableVisualization); } - start(core: CoreStart, { uiActions }: DatatableVisualizationPluginStartPlugins) { - setExecuteTriggerActions(uiActions.executeTriggerActions); - } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 78b038cf702f84..f9c5668ca1e069 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -21,6 +21,9 @@ import { import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; import { DragDrop } from '../../drag_drop'; import { FrameLayout } from './frame_layout'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; function generateSuggestion(state = {}): DatasourceSuggestion { return { @@ -48,6 +51,11 @@ function getDefaultProps() { query: { query: '', language: 'lucene' }, filters: [], core: coreMock.createSetup(), + plugins: { + uiActions: uiActionsPluginMock.createStartContract(), + data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + }, }; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 6da9a947110818..06d417ad18d54b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -26,6 +26,7 @@ import { getSavedObjectFormat } from './save'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; import { generateId } from '../../id_generator'; import { Filter, Query, SavedQuery } from '../../../../../../src/plugins/data/public'; +import { EditorFrameStartPlugins } from '../service'; export interface EditorFrameProps { doc?: Document; @@ -36,6 +37,7 @@ export interface EditorFrameProps { ExpressionRenderer: ReactExpressionRendererType; onError: (e: { message: string }) => void; core: CoreSetup | CoreStart; + plugins: EditorFrameStartPlugins; dateRange: { fromDate: string; toDate: string; @@ -285,6 +287,7 @@ export function EditorFrame(props: EditorFrameProps) { dispatch={dispatch} ExpressionRenderer={props.ExpressionRenderer} core={props.core} + plugins={props.plugins} /> ) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index 1f62929783b631..71aabaae3c65c5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -9,6 +9,9 @@ import { EditorFrameProps } from './index'; import { Datasource, Visualization } from '../../types'; import { createExpressionRendererMock } from '../mocks'; import { coreMock } from 'src/core/public/mocks'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; describe('editor_frame state management', () => { describe('initialization', () => { @@ -24,6 +27,11 @@ describe('editor_frame state management', () => { ExpressionRenderer: createExpressionRendererMock(), onChange: jest.fn(), core: coreMock.createSetup(), + plugins: { + uiActions: uiActionsPluginMock.createStartContract(), + data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + }, dateRange: { fromDate: 'now-7d', toDate: 'now' }, query: { query: '', language: 'lucene' }, filters: [], diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx index 33ecee53fa3bc2..a20626ebaaad7c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx @@ -22,6 +22,10 @@ import { DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { Ast } from '@kbn/interpreter/common'; import { coreMock } from 'src/core/public/mocks'; import { esFilters, IFieldType, IIndexPattern } from '../../../../../../src/plugins/data/public'; +import { TriggerId, UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { TriggerContract } from '../../../../../../src/plugins/ui_actions/public/triggers'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; describe('workspace_panel', () => { let mockVisualization: jest.Mocked; @@ -29,10 +33,15 @@ describe('workspace_panel', () => { let mockDatasource: DatasourceMock; let expressionRendererMock: jest.Mock; + let uiActionsMock: jest.Mocked; + let trigger: jest.Mocked>; let instance: ReactWrapper; beforeEach(() => { + trigger = ({ exec: jest.fn() } as unknown) as jest.Mocked>; + uiActionsMock = uiActionsPluginMock.createStartContract(); + uiActionsMock.getTrigger.mockReturnValue(trigger); mockVisualization = createMockVisualization(); mockVisualization2 = createMockVisualization(); @@ -60,6 +69,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -82,6 +92,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -104,6 +115,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -140,6 +152,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -198,6 +211,48 @@ describe('workspace_panel', () => { `); }); + it('should execute a trigger on expression event', () => { + const framePublicAPI = createMockFramePublicAPI(); + framePublicAPI.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + mockDatasource.toExpression.mockReturnValue('datasource'); + mockDatasource.getLayers.mockReturnValue(['first']); + + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} + /> + ); + + const onEvent = expressionRendererMock.mock.calls[0][0].onEvent!; + + const eventData = {}; + onEvent({ name: 'brush', data: eventData }); + + expect(uiActionsMock.getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush); + expect(trigger.exec).toHaveBeenCalledWith({ data: eventData }); + }); + it('should include data fetching for each layer in the expression', () => { const mockDatasource2 = createMockDatasource('a'); const framePublicAPI = createMockFramePublicAPI(); @@ -237,6 +292,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -316,6 +372,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -370,6 +427,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -424,6 +482,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -461,6 +520,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -504,6 +564,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -559,6 +620,7 @@ describe('workspace_panel', () => { dispatch={mockDispatch} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx index e246d8e27a7089..b000fc7fa0176a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx @@ -17,14 +17,25 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { CoreStart, CoreSetup } from 'kibana/public'; -import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from '../../../../../../src/plugins/expressions/public'; import { Action } from './state_management'; -import { Datasource, Visualization, FramePublicAPI } from '../../types'; +import { + Datasource, + Visualization, + FramePublicAPI, + isLensBrushEvent, + isLensFilterEvent, +} from '../../types'; import { DragDrop, DragContext } from '../../drag_drop'; import { getSuggestions, switchToSuggestion } from './suggestion_helpers'; import { buildExpression } from './expression_helpers'; import { debouncedComponent } from '../../debounced_component'; import { trackUiEvent } from '../../lens_ui_telemetry'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public'; export interface WorkspacePanelProps { activeVisualizationId: string | null; @@ -43,6 +54,7 @@ export interface WorkspacePanelProps { dispatch: (action: Action) => void; ExpressionRenderer: ReactExpressionRendererType; core: CoreStart | CoreSetup; + plugins: { uiActions?: UiActionsStart }; } export const WorkspacePanel = debouncedComponent(InnerWorkspacePanel); @@ -58,6 +70,7 @@ export function InnerWorkspacePanel({ framePublicAPI, dispatch, core, + plugins, ExpressionRenderer: ExpressionRendererComponent, }: WorkspacePanelProps) { const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); @@ -211,6 +224,22 @@ export function InnerWorkspacePanel({ className="lnsExpressionRenderer__component" padding="m" expression={expression!} + onEvent={(event: ExpressionRendererEvent) => { + if (!plugins.uiActions) { + // ui actions not available, not handling event... + return; + } + if (isLensBrushEvent(event)) { + plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + }); + } + if (isLensFilterEvent(event)) { + plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + }); + } + }} renderError={(errorMessage?: string | null) => { return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index 8d95540b3e8b51..4e5b32ad7f7a35 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -10,6 +10,7 @@ import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; import { Document } from '../../persistence'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; jest.mock('../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -34,10 +35,14 @@ const savedVis: Document = { describe('embeddable', () => { let mountpoint: HTMLDivElement; let expressionRenderer: jest.Mock; + let getTrigger: jest.Mock; + let trigger: { exec: jest.Mock }; beforeEach(() => { mountpoint = document.createElement('div'); expressionRenderer = jest.fn(_props => null); + trigger = { exec: jest.fn() }; + getTrigger = jest.fn(() => trigger); }); afterEach(() => { @@ -48,6 +53,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editPath: '', editUrl: '', @@ -70,6 +76,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editPath: '', editUrl: '', @@ -97,6 +104,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editPath: '', editUrl: '', @@ -114,6 +122,32 @@ describe('embeddable', () => { }); }); + it('should execute trigger on event from expression renderer', () => { + const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, + expressionRenderer, + getTrigger, + { + editPath: '', + editUrl: '', + editable: true, + savedVis, + }, + { id: '123' } + ); + embeddable.render(mountpoint); + + const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; + + const eventData = {}; + onEvent({ name: 'brush', data: eventData }); + + expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush); + expect(trigger.exec).toHaveBeenCalledWith( + expect.objectContaining({ data: eventData, embeddable: expect.anything() }) + ); + }); + it('should not re-render if only change is in disabled filter', () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; @@ -122,6 +156,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editPath: '', editUrl: '', @@ -154,6 +189,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( timefilter, expressionRenderer, + getTrigger, { editPath: '', editUrl: '', diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 559854cbab39a3..796cf5b32e3ba8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -8,25 +8,30 @@ import _ from 'lodash'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { - Query, - TimeRange, Filter, IIndexPattern, + Query, TimefilterContract, + TimeRange, } from 'src/plugins/data/public'; import { Subscription } from 'rxjs'; -import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from '../../../../../../src/plugins/expressions/public'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public'; import { Embeddable as AbstractEmbeddable, + EmbeddableInput, EmbeddableOutput, IContainer, - EmbeddableInput, } from '../../../../../../src/plugins/embeddable/public'; -import { Document, DOC_TYPE } from '../../persistence'; +import { DOC_TYPE, Document } from '../../persistence'; import { ExpressionWrapper } from './expression_wrapper'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { isLensBrushEvent, isLensFilterEvent } from '../../types'; export interface LensEmbeddableConfiguration { savedVis: Document; @@ -50,6 +55,7 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(input)); @@ -100,6 +108,9 @@ export class Embeddable extends AbstractEmbeddable, domNode ); } + handleEvent = (event: ExpressionRendererEvent) => { + if (!this.getTrigger || this.input.disableTriggers) { + return; + } + if (isLensBrushEvent(event)) { + this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + embeddable: this, + }); + } + if (isLensFilterEvent(event)) { + this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + embeddable: this, + }); + } + }; + destroy() { super.destroy(); if (this.domNode) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 852d3779158569..c23d44aa8e4b60 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -26,6 +26,7 @@ import { import { Embeddable } from './embeddable'; import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; import { getEditPath } from '../../../common'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; interface StartServices { timefilter: TimefilterContract; @@ -34,6 +35,7 @@ interface StartServices { savedObjectsClient: SavedObjectsClientContract; expressionRenderer: ReactExpressionRendererType; indexPatternService: IndexPatternsContract; + uiActions?: UiActionsStart; } export class EmbeddableFactory implements EmbeddableFactoryDefinition { @@ -74,6 +76,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { indexPatternService, timefilter, expressionRenderer, + uiActions, } = await this.getStartServices(); const store = new SavedObjectIndexStore(savedObjectsClient); const savedVis = await store.load(savedObjectId); @@ -99,6 +102,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { return new Embeddable( timefilter, expressionRenderer, + uiActions?.getTrigger, { savedVis, editPath: getEditPath(savedObjectId), diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx index 49c91affe3dc4a..41706121830cb3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx @@ -9,7 +9,10 @@ import { I18nProvider } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon } from '@elastic/eui'; import { TimeRange, Filter, Query } from 'src/plugins/data/public'; -import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from 'src/plugins/expressions/public'; export interface ExpressionWrapperProps { ExpressionRenderer: ReactExpressionRendererType; @@ -20,12 +23,14 @@ export interface ExpressionWrapperProps { filters?: Filter[]; lastReloadRequestTime?: number; }; + handleEvent: (event: ExpressionRendererEvent) => void; } export function ExpressionWrapper({ ExpressionRenderer: ExpressionRendererComponent, expression, context, + handleEvent, }: ExpressionWrapperProps) { return ( @@ -51,6 +56,7 @@ export function ExpressionWrapper({ expression={expression} searchContext={{ ...context }} renderError={error =>
{error}
} + onEvent={handleEvent} />
)} diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 15fe449d6563b4..a815e70c58629a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -26,6 +26,7 @@ import { mergeTables } from './merge_tables'; import { formatColumn } from './format_column'; import { EmbeddableFactory } from './embeddable/embeddable_factory'; import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; export interface EditorFrameSetupPlugins { data: DataPublicPluginSetup; @@ -37,6 +38,7 @@ export interface EditorFrameStartPlugins { data: DataPublicPluginStart; embeddable?: EmbeddableStart; expressions: ExpressionsStart; + uiActions?: UiActionsStart; } async function collectAsyncDefinitions( @@ -73,6 +75,7 @@ export class EditorFrameService { timefilter: deps.data.query.timefilter.timefilter, expressionRenderer: deps.expressions.ReactExpressionRenderer, indexPatternService: deps.data.indexPatterns, + uiActions: deps.uiActions, }; }; @@ -116,6 +119,7 @@ export class EditorFrameService { (doc && doc.visualizationType) || firstVisualizationId || null } core={core} + plugins={plugins} ExpressionRenderer={plugins.expressions.ReactExpressionRenderer} doc={doc} dateRange={dateRange} diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/pie_visualization/index.ts index b2aae2e8529a58..dd828c6c35300f 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/pie_visualization/index.ts @@ -5,13 +5,12 @@ */ import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; -import { CoreSetup, CoreStart } from 'src/core/public'; +import { CoreSetup } from 'src/core/public'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { pieVisualization } from './pie_visualization'; import { pie, getPieRenderer } from './register_expression'; import { EditorFrameSetup, FormatFactory } from '../types'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { setExecuteTriggerActions } from '../services'; export interface PieVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; @@ -44,10 +43,4 @@ export class PieVisualization { editorFrame.registerVisualization(pieVisualization); } - - start(core: CoreStart, { uiActions }: PieVisualizationPluginStartPlugins) { - setExecuteTriggerActions(uiActions.executeTriggerActions); - } - - stop() {} } diff --git a/x-pack/plugins/lens/public/pie_visualization/register_expression.tsx b/x-pack/plugins/lens/public/pie_visualization/register_expression.tsx index 7babf7ed7ff465..bbc6a1dc75c3a6 100644 --- a/x-pack/plugins/lens/public/pie_visualization/register_expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/register_expression.tsx @@ -14,9 +14,8 @@ import { ExpressionRenderDefinition, ExpressionFunctionDefinition, } from 'src/plugins/expressions/public'; -import { LensMultiTable, FormatFactory } from '../types'; +import { LensMultiTable, FormatFactory, LensFilterEvent } from '../types'; import { PieExpressionProps, PieExpressionArgs } from './types'; -import { getExecuteTriggerActions } from '../services'; import { PieComponent } from './render_function'; export interface PieRender { @@ -109,7 +108,9 @@ export const getPieRenderer = (dependencies: { config: PieExpressionProps, handlers: IInterpreterRenderHandlers ) => { - const executeTriggerActions = getExecuteTriggerActions(); + const onClickValue = (data: LensFilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; const formatFactory = await dependencies.formatFactory; ReactDOM.render( @@ -117,7 +118,7 @@ export const getPieRenderer = (dependencies: { {...config} {...dependencies} formatFactory={formatFactory} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} isDarkMode={dependencies.isDarkMode} /> , diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index b0d4e0d2cc52b6..a914efcead005a 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Settings } from '@elastic/charts'; +import { SeriesIdentifier, Settings } from '@elastic/charts'; import { shallow } from 'enzyme'; import { LensMultiTable } from '../types'; import { PieComponent } from './render_function'; @@ -59,7 +59,7 @@ describe('PieVisualization component', () => { formatFactory: getFormatSpy, isDarkMode: false, chartTheme: {}, - executeTriggerActions: jest.fn(), + onClickValue: jest.fn(), }; } @@ -111,6 +111,58 @@ describe('PieVisualization component', () => { expect(component.find(Settings).prop('legendMaxDepth')).toBeUndefined(); }); + test('it calls filter callback with the given context', () => { + const defaultArgs = getDefaultArgs(); + const component = shallow(); + component + .find(Settings) + .first() + .prop('onElementClick')!([[[{ groupByRollup: 6, value: 6 }], {} as SeriesIdentifier]]); + + expect(defaultArgs.onClickValue.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "data": Array [ + Object { + "column": 0, + "row": 0, + "table": Object { + "columns": Array [ + Object { + "id": "a", + "name": "a", + }, + Object { + "id": "b", + "name": "b", + }, + Object { + "id": "c", + "name": "c", + }, + ], + "rows": Array [ + Object { + "a": 6, + "b": 2, + "c": "I", + "d": "Row 1", + }, + Object { + "a": 1, + "b": 5, + "c": "J", + "d": "Row 2", + }, + ], + "type": "kibana_datatable", + }, + "value": 6, + }, + ], + } + `); + }); + test('it shows emptyPlaceholder for undefined grouped data', () => { const defaultData = getDefaultArgs().data; const emptyData: LensMultiTable = { diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 56019b3e6c8912..d812803272f3e1 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -24,12 +24,10 @@ import { RecursivePartial, LayerValue, } from '@elastic/charts'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { FormatFactory } from '../types'; +import { FormatFactory, LensFilterEvent } from '../types'; import { VisualizationContainer } from '../visualization_container'; import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants'; import { ColumnGroups, PieExpressionProps } from './types'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { getSliceValueWithFallback, getFilterContext } from './render_helpers'; import { EmptyPlaceholder } from '../shared_components'; import './visualization.scss'; @@ -43,13 +41,13 @@ export function PieComponent( formatFactory: FormatFactory; chartTheme: Exclude; isDarkMode: boolean; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (data: LensFilterEvent['data']) => void; } ) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; - const { chartTheme, isDarkMode, executeTriggerActions } = props; + const { chartTheme, isDarkMode, onClickValue } = props; const { shape, groups, @@ -246,7 +244,7 @@ export function PieComponent( firstTable ); - executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + onClickValue(context); }} /> { ], }; expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a'], table)).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + ], }); }); @@ -124,16 +122,14 @@ describe('render helpers', () => { ], }; expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a', 'b'], table)).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + ], }); }); @@ -161,22 +157,20 @@ describe('render helpers', () => { table ) ).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - { - row: 1, - column: 1, - value: 'Two', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + { + row: 1, + column: 1, + value: 'Two', + table, + }, + ], }); }); }); diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts index bc3c29ba0fff15..3f7494661c049e 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts @@ -6,8 +6,8 @@ import { Datum, LayerValue } from '@elastic/charts'; import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public'; -import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; import { ColumnGroups } from './types'; +import { LensFilterEvent } from '../types'; export function getSliceValueWithFallback( d: Datum, @@ -28,7 +28,7 @@ export function getFilterContext( clickedLayers: LayerValue[], layerColumnIds: string[], table: KibanaDatatable -): ValueClickTriggerContext { +): LensFilterEvent['data'] { const matchingIndex = table.rows.findIndex(row => clickedLayers.every((layer, index) => { const columnId = layerColumnIds[index]; @@ -37,13 +37,11 @@ export function getFilterContext( ); return { - data: { - data: clickedLayers.map((clickedLayer, index) => ({ - column: table.columns.findIndex(col => col.id === layerColumnIds[index]), - row: matchingIndex, - value: clickedLayer.groupByRollup, - table, - })), - }, + data: clickedLayers.map((clickedLayer, index) => ({ + column: table.columns.findIndex(col => col.id === layerColumnIds[index]), + row: matchingIndex, + value: clickedLayer.groupByRollup, + table, + })), }; } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index b2309657967f1e..f9a577e001c64e 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -103,9 +103,6 @@ export class LensPlugin { start(core: CoreStart, startDependencies: LensPluginStartDependencies) { this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; - this.xyVisualization.start(core, startDependencies); - this.datatableVisualization.start(core, startDependencies); - this.pieVisualization.start(core, startDependencies); } stop() { diff --git a/x-pack/plugins/lens/public/services.ts b/x-pack/plugins/lens/public/services.ts deleted file mode 100644 index a66743dde26619..00000000000000 --- a/x-pack/plugins/lens/public/services.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createGetterSetter } from '../../../../src/plugins/kibana_utils/public'; -import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; - -export const [getExecuteTriggerActions, setExecuteTriggerActions] = createGetterSetter< - UiActionsStart['executeTriggerActions'] ->('executeTriggerActions'); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 04efc642793b08..42dcce0e438d71 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -7,11 +7,21 @@ import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { CoreSetup } from 'kibana/public'; -import { KibanaDatatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + IInterpreterRenderHandlers, + KibanaDatatable, + SerializedFieldFormat, +} from '../../../../src/plugins/expressions/public'; import { DragContextState } from './drag_drop'; import { Document } from './persistence'; import { DateRange } from '../common'; import { Query, Filter, SavedQuery, IFieldFormat } from '../../../../src/plugins/data/public'; +import { + SELECT_RANGE_TRIGGER, + TriggerContext, + VALUE_CLICK_TRIGGER, +} from '../../../../src/plugins/ui_actions/public'; export type ErrorCallback = (e: { message: string }) => void; @@ -467,3 +477,29 @@ export interface Visualization { */ toPreviewExpression?: (state: T, frame: FramePublicAPI) => Ast | string | null; } + +export interface LensFilterEvent { + name: 'filter'; + data: TriggerContext['data']; +} +export interface LensBrushEvent { + name: 'brush'; + data: TriggerContext['data']; +} + +export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent { + return event.name === 'filter'; +} + +export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensBrushEvent { + return event.name === 'brush'; +} + +/** + * Expression renderer handlers specifically for lens renderers. This is a narrowed down + * version of the general render handlers, specifying supported event types. If this type is + * used, dispatched events will be handled correctly. + */ +export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { + event: (event: LensFilterEvent | LensBrushEvent) => void; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 9a0819d4f01c44..23cf9e7ff818fc 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -5,15 +5,13 @@ */ import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; -import { CoreSetup, IUiSettingsClient, CoreStart } from 'kibana/public'; +import { CoreSetup, IUiSettingsClient } from 'kibana/public'; import moment from 'moment-timezone'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { xyVisualization } from './xy_visualization'; import { xyChart, getXyChartRenderer } from './xy_expression'; import { legendConfig, xConfig, layerConfig } from './types'; import { EditorFrameSetup, FormatFactory } from '../types'; -import { setExecuteTriggerActions } from '../services'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -21,10 +19,6 @@ export interface XyVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; } -interface XyVisualizationPluginStartPlugins { - uiActions: UiActionsStart; -} - function getTimeZone(uiSettings: IUiSettingsClient) { const configuredTimeZone = uiSettings.get('dateFormat:tz'); if (configuredTimeZone === 'Browser') { @@ -59,7 +53,4 @@ export class XyVisualization { editorFrame.registerVisualization(xyVisualization); } - start(core: CoreStart, { uiActions }: XyVisualizationPluginStartPlugins) { - setExecuteTriggerActions(uiActions.executeTriggerActions); - } } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index 0f9aa1c10e127a..72e51b175543c5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -25,7 +25,8 @@ import { XYArgs, LegendConfig, legendConfig, layerConfig, LayerArgs } from './ty import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -const executeTriggerActions = jest.fn(); +const onClickValue = jest.fn(); +const onSelectRange = jest.fn(); const dateHistogramData: LensMultiTable = { type: 'lens_multitable', @@ -296,7 +297,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -344,7 +346,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` @@ -379,7 +382,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -415,7 +419,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -452,7 +457,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -496,7 +502,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -530,7 +537,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toBeUndefined(); @@ -546,7 +554,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -563,7 +572,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -580,7 +590,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -602,7 +613,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -611,12 +623,10 @@ describe('xy_expression', () => { .first() .prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); - expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', { - data: { - column: 0, - table: dateHistogramData.tables.timeLayer, - range: [1585757732783, 1585758880838], - }, + expect(onSelectRange).toHaveBeenCalledWith({ + column: 0, + table: dateHistogramData.tables.timeLayer, + range: [1585757732783, 1585758880838], timeFieldName: 'order_date', }); }); @@ -656,7 +666,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -665,23 +676,21 @@ describe('xy_expression', () => { .first() .prop('onElementClick')!([[geometry, series as XYChartSeriesIdentifier]]); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 1, - row: 1, - table: data.tables.first, - value: 5, - }, - { - column: 1, - row: 0, - table: data.tables.first, - value: 2, - }, - ], - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 1, + table: data.tables.first, + value: 5, + }, + { + column: 1, + row: 0, + table: data.tables.first, + value: 2, + }, + ], }); }); @@ -695,7 +704,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -713,7 +723,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -734,7 +745,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -753,7 +765,8 @@ describe('xy_expression', () => { timeZone="CEST" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('timeZone')).toEqual('CEST'); @@ -771,7 +784,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true); @@ -796,7 +810,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true); @@ -815,7 +830,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(false); @@ -876,7 +892,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); }; @@ -1071,7 +1088,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('xScaleType')).toEqual(ScaleType.Ordinal); @@ -1088,7 +1106,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('yScaleType')).toEqual(ScaleType.Sqrt); @@ -1105,7 +1124,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1123,7 +1143,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1141,7 +1162,8 @@ describe('xy_expression', () => { chartTheme={{}} histogramBarTarget={50} timeZone="UTC" - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(getFormatSpy).toHaveBeenCalledWith({ @@ -1161,7 +1183,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1248,7 +1271,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1302,7 +1326,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index d598b9c7406550..cb2defbc54f49f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -21,24 +21,22 @@ import { } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; import { - IInterpreterRenderHandlers, - ExpressionRenderDefinition, ExpressionFunctionDefinition, + ExpressionRenderDefinition, ExpressionValueSearchContext, } from 'src/plugins/expressions/public'; import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { - ValueClickTriggerContext, - RangeSelectTriggerContext, -} from '../../../../../src/plugins/embeddable/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { LensMultiTable, FormatFactory } from '../types'; + LensMultiTable, + FormatFactory, + ILensInterpreterRenderHandlers, + LensFilterEvent, + LensBrushEvent, +} from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; -import { getExecuteTriggerActions } from '../services'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { parseInterval } from '../../../../../src/plugins/data/common'; import { EmptyPlaceholder } from '../shared_components'; @@ -63,7 +61,8 @@ type XYChartRenderProps = XYChartProps & { formatFactory: FormatFactory; timeZone: string; histogramBarTarget: number; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (data: LensFilterEvent['data']) => void; + onSelectRange: (data: LensBrushEvent['data']) => void; }; export const xyChart: ExpressionFunctionDefinition< @@ -125,9 +124,18 @@ export const getXyChartRenderer = (dependencies: { }), validate: () => undefined, reuseDomNode: true, - render: async (domNode: Element, config: XYChartProps, handlers: IInterpreterRenderHandlers) => { - const executeTriggerActions = getExecuteTriggerActions(); + render: async ( + domNode: Element, + config: XYChartProps, + handlers: ILensInterpreterRenderHandlers + ) => { handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + const onClickValue = (data: LensFilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; + const onSelectRange = (data: LensBrushEvent['data']) => { + handlers.event({ name: 'brush', data }); + }; const formatFactory = await dependencies.formatFactory; ReactDOM.render( @@ -137,7 +145,8 @@ export const getXyChartRenderer = (dependencies: { chartTheme={dependencies.chartTheme} timeZone={dependencies.timeZone} histogramBarTarget={dependencies.histogramBarTarget} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> , domNode, @@ -177,7 +186,8 @@ export function XYChart({ timeZone, chartTheme, histogramBarTarget, - executeTriggerActions, + onClickValue, + onSelectRange, }: XYChartRenderProps) { const { legend, layers } = args; @@ -287,15 +297,13 @@ export function XYChart({ ); const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; - const context: RangeSelectTriggerContext = { - data: { - range: [min, max], - table, - column: xAxisColumnIndex, - }, + const context: LensBrushEvent['data'] = { + range: [min, max], + table, + column: xAxisColumnIndex, timeFieldName, }; - executeTriggerActions(VIS_EVENT_TO_TRIGGER.brush, context); + onSelectRange(context); }} onElementClick={([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue @@ -337,18 +345,16 @@ export function XYChart({ ?.aggConfigParams?.field; const timeFieldName = xDomain && xAxisFieldName; - const context: ValueClickTriggerContext = { - data: { - data: points.map(point => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), - }, + const context: LensFilterEvent['data'] = { + data: points.map(point => ({ + row: point.row, + column: point.column, + value: point.value, + table, + })), timeFieldName, }; - executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + onClickValue(context); }} /> diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap index 03421e66c77f59..e4411807dfa56b 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AddLicense component when license is active should display correct verbiage 1`] = `""`; +exports[`AddLicense component when license is active should display correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; -exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; +exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap index 9ac8b14236685b..b621e89efbee3f 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap @@ -958,11 +958,11 @@ exports[`UploadLicense should display a modal when license requires acknowledgem className="euiFlexItem euiFlexItem--flexGrowZero" > { const [core] = await getStartServices(); const initialLicense = await plugins.licensing.license$.pipe(first()).toPromise(); diff --git a/x-pack/plugins/licensing/public/plugin.test.ts b/x-pack/plugins/licensing/public/plugin.test.ts index f68e1dcfaf62ba..9f0019680d14bf 100644 --- a/x-pack/plugins/licensing/public/plugin.test.ts +++ b/x-pack/plugins/licensing/public/plugin.test.ts @@ -367,7 +367,7 @@ describe('licensing plugin', () => { expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1); expect(mountExpiredBannerMock).toHaveBeenCalledWith({ type: 'gold', - uploadUrl: '/app/kibana#/management/elasticsearch/license_management/upload_license', + uploadUrl: '/app/kibana#/management/stack/license_management/upload_license', }); }); }); diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index dab4c4048ce4c4..31910d81b34342 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -148,7 +148,7 @@ export class LicensingPlugin implements Plugin { private showExpiredBanner(license: ILicense) { const uploadUrl = this.coreStart!.http.basePath.prepend( - '/app/kibana#/management/elasticsearch/license_management/upload_license' + '/app/kibana#/management/stack/license_management/upload_license' ); this.coreStart!.overlays.banners.add( mountExpiredBanner({ diff --git a/x-pack/plugins/lists/common/constants.ts b/x-pack/plugins/lists/common/constants.ts index dbe31fed664131..96d28bf618ce4d 100644 --- a/x-pack/plugins/lists/common/constants.ts +++ b/x-pack/plugins/lists/common/constants.ts @@ -5,8 +5,14 @@ */ /** - * Lists routes + * Value list routes */ -export const LIST_URL = `/api/lists`; +export const LIST_URL = '/api/lists'; export const LIST_INDEX = `${LIST_URL}/index`; export const LIST_ITEM_URL = `${LIST_URL}/items`; + +/** + * Exception list routes + */ +export const EXCEPTION_LIST_URL = '/api/exception_lists'; +export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index edc037ed7a0b12..cd69685ffcb1b2 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { NonEmptyString } from '../types/non_empty_string'; +import { DefaultStringArray, NonEmptyString } from '../types'; export const name = t.string; export type Name = t.TypeOf; @@ -21,8 +21,9 @@ export const descriptionOrUndefined = t.union([description, t.undefined]); export type DescriptionOrUndefined = t.TypeOf; export const list_id = NonEmptyString; +export type ListId = t.TypeOf; export const list_idOrUndefined = t.union([list_id, t.undefined]); -export type List_idOrUndefined = t.TypeOf; +export type ListIdOrUndefined = t.TypeOf; export const item = t.string; export const created_at = t.string; // TODO: Make this into an ISO Date string check @@ -60,3 +61,44 @@ export type MetaOrUndefined = t.TypeOf; export const esDataTypeUnion = t.union([t.type({ ip }), t.type({ keyword })]); export type EsDataTypeUnion = t.TypeOf; + +export const tags = DefaultStringArray; +export type Tags = t.TypeOf; +export const tagsOrUndefined = t.union([tags, t.undefined]); +export type TagsOrUndefined = t.TypeOf; + +export const _tags = DefaultStringArray; +export type _Tags = t.TypeOf; +export const _tagsOrUndefined = t.union([_tags, t.undefined]); +export type _TagsOrUndefined = t.TypeOf; + +// TODO: Change this into a t.keyof enumeration when we know what types of lists we going to have. +export const exceptionListType = t.string; +export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); +export type ExceptionListType = t.TypeOf; +export type ExceptionListTypeOrUndefined = t.TypeOf; + +// TODO: Change this into a t.keyof enumeration when we know what types of lists we going to have. +export const exceptionListItemType = t.string; +export type ExceptionListItemType = t.TypeOf; + +export const list_type = t.keyof({ item: null, list: null }); +export type ListType = t.TypeOf; + +// TODO: Investigate what the deep structure of a comment is really going to be and then change this to use that deep structure with a default array +export const comment = DefaultStringArray; +export type Comment = t.TypeOf; +export const commentOrUndefined = t.union([comment, t.undefined]); +export type CommentOrUndefined = t.TypeOf; + +export const item_id = NonEmptyString; +export type ItemId = t.TypeOf; +export const itemIdOrUndefined = t.union([item_id, t.undefined]); +export type ItemIdOrUndefined = t.TypeOf; + +export const per_page = t.number; // TODO: Change this out for PositiveNumber from siem +export const total = t.number; // TODO: Change this out for PositiveNumber from siem +export const page = t.number; // TODO: Change this out for PositiveNumber from siem +export const sort_field = t.string; +export const sort_order = t.keyof({ asc: null, desc: null }); +export const filter = t.string; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts index 8f23f3744e563d..d008a823081534 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts @@ -16,6 +16,7 @@ import { updated_by, } from '../common/schemas'; +// TODO: Should we use partial here and everywhere these are instead of this OrUndefined? export const updateEsListSchema = t.exact( t.type({ description: descriptionOrUndefined, diff --git a/x-pack/plugins/lists/common/schemas/index.ts b/x-pack/plugins/lists/common/schemas/index.ts index 6a60a6df55691a..774378c6318f5d 100644 --- a/x-pack/plugins/lists/common/schemas/index.ts +++ b/x-pack/plugins/lists/common/schemas/index.ts @@ -5,7 +5,9 @@ */ export * from './common'; -export * from './request'; -export * from './response'; export * from './elastic_query'; export * from './elastic_response'; +export * from './request'; +export * from './response'; +export * from './saved_objects'; +export * from './types'; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts new file mode 100644 index 00000000000000..f899fd69110fa2 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + ItemId, + Tags, + _Tags, + _tags, + comment, + description, + exceptionListItemType, + list_id, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultEntryArray, DefaultUuid } from '../types'; +import { EntriesArray } from '../types/entries'; + +export const createExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + list_id, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + comment, // defaults to empty array if not set during decode + entries: DefaultEntryArray, // defaults to empty array if not set during decode + item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateExceptionListItemSchemaPartial = Identity< + t.TypeOf +>; +export type CreateExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays +// and if a item_id is not specified it turns into a default GUID +export type CreateExceptionListItemSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts new file mode 100644 index 00000000000000..d38d3cc0385259 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DESCRIPTION, LIST_ID, META, NAME, TYPE } from '../../constants.mock'; + +import { CreateExceptionListSchema } from './create_exception_list_schema'; + +export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ + _tags: [], + description: DESCRIPTION, + list_id: LIST_ID, + meta: META, + name: NAME, + tags: [], + type: TYPE, +}); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts new file mode 100644 index 00000000000000..f19c50c6b42aed --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps'; + +import { + CreateExceptionListSchema, + createExceptionListSchema, +} from './create_exception_list_schema'; +import { getCreateExceptionListSchemaMock } from './create_exception_list_schema.mock'; + +describe('create_exception_list_schema', () => { + test('it should validate a typical exception lists request', () => { + const payload = getCreateExceptionListSchemaMock(); + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for meta', () => { + const payload = getCreateExceptionListSchemaMock(); + delete payload.meta; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for tags but return an array', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + const outputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for _tags but return an array', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + const outputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload._tags; + outputPayload._tags = []; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for list_id and auto generate a uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as CreateExceptionListSchema).list_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + test('it should accept an undefined for list_id and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateExceptionListSchema & { + extraKey?: string; + } = getCreateExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts new file mode 100644 index 00000000000000..5ba3bf4e8f43bf --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + ListId, + Tags, + _Tags, + _tags, + description, + exceptionListType, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultUuid } from '../types/default_uuid'; + +export const createExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateExceptionListSchemaPartial = Identity>; +export type CreateExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays. +export type CreateExceptionListSchemaDecoded = Identity< + CreateExceptionListSchema & { + _tags: _Tags; + tags: Tags; + list_id: ListId; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts index 6cba81e47fbcc2..6d16f2074864c7 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_id, metaOrUndefined, value } from '../common/schemas'; +import { id, list_id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const createListItemSchema = t.intersection([ @@ -18,7 +18,7 @@ export const createListItemSchema = t.intersection([ value, }) ), - t.exact(t.partial({ id: idOrUndefined, meta: metaOrUndefined })), + t.exact(t.partial({ id, meta })), ]); export type CreateListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts index 7a6e2a707873cb..68df80b2a42dd1 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts @@ -6,7 +6,7 @@ import * as t from 'io-ts'; -import { description, idOrUndefined, metaOrUndefined, name, type } from '../common/schemas'; +import { description, id, meta, name, type } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const createListSchema = t.intersection([ @@ -17,7 +17,7 @@ export const createListSchema = t.intersection([ type, }) ), - t.exact(t.partial({ id: idOrUndefined, meta: metaOrUndefined })), + t.exact(t.partial({ id, meta })), ]); export type CreateListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts new file mode 100644 index 00000000000000..607e05ef8286f6 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, item_id } from '../common/schemas'; + +export const deleteExceptionListItemSchema = t.exact( + t.partial({ + id, + item_id, + }) +); + +export type DeleteExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts new file mode 100644 index 00000000000000..7a6086514f9432 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, list_id } from '../common/schemas'; + +export const deleteExceptionListSchema = t.exact( + t.partial({ + id, + list_id, + }) +); + +export type DeleteExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts index 96f054b304962c..91887395e747dd 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, list_id, valueOrUndefined } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const deleteListItemSchema = t.intersection([ @@ -17,7 +17,7 @@ export const deleteListItemSchema = t.intersection([ value: valueOrUndefined, }) ), - t.exact(t.partial({ id: idOrUndefined, list_id: list_idOrUndefined })), + t.exact(t.partial({ id, list_id })), ]); export type DeleteListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts new file mode 100644 index 00000000000000..3fc51dd20b0b35 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { filter, list_id, page, per_page, sort_field, sort_order } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const findExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + list_id, + }) + ), + t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + page, // defaults to undefined if not set during decode + per_page, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) + ), +]); + +export type FindExceptionListItemSchemaPartial = t.TypeOf; + +export type FindExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts new file mode 100644 index 00000000000000..f795be9493fbfc --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { filter, page, per_page, sort_field, sort_order } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const findExceptionListSchema = t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + page, // defaults to undefined if not set during decode + per_page, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) +); + +export type FindExceptionListSchemaPartial = t.TypeOf; + +export type FindExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts index c1745dda7afabe..73d9a53a41e4f1 100644 --- a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts @@ -8,12 +8,10 @@ import * as t from 'io-ts'; -import { list_idOrUndefined, typeOrUndefined } from '../common/schemas'; +import { list_id, type } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; -export const importListItemQuerySchema = t.exact( - t.partial({ list_id: list_idOrUndefined, type: typeOrUndefined }) -); +export const importListItemQuerySchema = t.exact(t.partial({ list_id, type })); export type ImportListItemQuerySchemaPartial = Identity>; export type ImportListItemQuerySchema = RequiredKeepUndefined< diff --git a/x-pack/plugins/lists/common/schemas/request/index.ts b/x-pack/plugins/lists/common/schemas/request/index.ts index d332ab1eb1bab3..0dbd9297b773ef 100644 --- a/x-pack/plugins/lists/common/schemas/request/index.ts +++ b/x-pack/plugins/lists/common/schemas/request/index.ts @@ -4,16 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './create_exception_list_item_schema'; +export * from './create_exception_list_schema'; export * from './create_list_item_schema'; export * from './create_list_schema'; +export * from './delete_exception_list_item_schema'; +export * from './delete_exception_list_schema'; export * from './delete_list_item_schema'; export * from './delete_list_schema'; export * from './export_list_item_query_schema'; +export * from './find_exception_list_item_schema'; +export * from './find_exception_list_schema'; export * from './import_list_item_schema'; export * from './patch_list_item_schema'; export * from './patch_list_schema'; +export * from './read_exception_list_item_schema'; +export * from './read_exception_list_schema'; export * from './read_list_item_schema'; export * from './read_list_schema'; +export * from './update_exception_list_item_schema'; +export * from './update_exception_list_schema'; export * from './import_list_item_query_schema'; export * from './update_list_schema'; +export * from './update_exception_list_schema'; export * from './update_list_item_schema'; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts index 536931f715f3f6..2016069f32fb3f 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { id, metaOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const patchListItemSchema = t.intersection([ @@ -17,7 +17,7 @@ export const patchListItemSchema = t.intersection([ id, }) ), - t.exact(t.partial({ meta: metaOrUndefined, value: valueOrUndefined })), + t.exact(t.partial({ meta, value })), ]); export type PatchListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts index 59d1a66a581a0e..653a42dc916530 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { descriptionOrUndefined, id, metaOrUndefined, nameOrUndefined } from '../common/schemas'; +import { description, id, meta, name } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const patchListSchema = t.intersection([ @@ -17,9 +17,7 @@ export const patchListSchema = t.intersection([ id, }) ), - t.exact( - t.partial({ description: descriptionOrUndefined, meta: metaOrUndefined, name: nameOrUndefined }) - ), + t.exact(t.partial({ description, meta, name })), ]); export type PatchListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts new file mode 100644 index 00000000000000..095fcd2f63b484 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, item_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const readExceptionListItemSchema = t.partial({ + id, + item_id, +}); + +export type ReadExceptionListItemSchemaPartial = t.TypeOf; +export type ReadExceptionListItemSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts new file mode 100644 index 00000000000000..5593e640f71acc --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, list_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; + +export const readExceptionListSchema = t.partial({ + id, + list_id, +}); + +export type ReadExceptionListSchemaPartial = t.TypeOf; +export type ReadExceptionListSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts index b69523b664fd70..394c1f1e2289af 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts @@ -8,12 +8,10 @@ import * as t from 'io-ts'; -import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; +import { id, list_id, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; -export const readListItemSchema = t.exact( - t.partial({ id: idOrUndefined, list_id: list_idOrUndefined, value: valueOrUndefined }) -); +export const readListItemSchema = t.exact(t.partial({ id, list_id, value })); export type ReadListItemSchemaPartial = Identity>; export type ReadListItemSchema = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts new file mode 100644 index 00000000000000..162406a6d65894 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + Tags, + _Tags, + _tags, + comment, + description, + exceptionListItemType, + id, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; +import { DefaultEntryArray } from '../types'; +import { EntriesArray } from '../types/entries'; + +export const updateExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + comment, // defaults to empty array if not set during decode + entries: DefaultEntryArray, // defaults to empty array if not set during decode + id, // defaults to undefined if not set during decode + item_id: t.union([t.string, t.undefined]), + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateExceptionListItemSchemaPartial = Identity< + t.TypeOf +>; +export type UpdateExceptionListItemSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays +// and if a item_id is not specified it turns into a default GUID +export type UpdateExceptionListItemSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + entries: EntriesArray; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts new file mode 100644 index 00000000000000..e8a0dcd4994a2b --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + Tags, + _Tags, + _tags, + description, + exceptionListType, + meta, + name, + tags, +} from '../common/schemas'; +import { Identity, RequiredKeepUndefined } from '../../types'; + +export const updateExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + _tags, // defaults to empty array if not set during decode + id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode + list_id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateExceptionListSchemaPartial = Identity>; +export type UpdateExceptionListSchema = RequiredKeepUndefined< + t.TypeOf +>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays. +export type UpdateExceptionListSchemaDecoded = Identity< + Omit & { + _tags: _Tags; + tags: Tags; + } +>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts index 23701ff753bc0d..3a42cf28665f5d 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { id, metaOrUndefined, value } from '../common/schemas'; +import { id, meta, value } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const updateListItemSchema = t.intersection([ @@ -18,7 +18,11 @@ export const updateListItemSchema = t.intersection([ value, }) ), - t.exact(t.partial({ meta: metaOrUndefined })), + t.exact( + t.partial({ + meta, // defaults to undefined if not set during decode + }) + ), ]); export type UpdateListItemSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts index 8223a6a34b7716..4c5c8c429a14cd 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; -import { description, id, metaOrUndefined, name } from '../common/schemas'; +import { description, id, meta, name } from '../common/schemas'; import { Identity, RequiredKeepUndefined } from '../../types'; export const updateListSchema = t.intersection([ @@ -19,7 +19,11 @@ export const updateListSchema = t.intersection([ name, }) ), - t.exact(t.partial({ meta: metaOrUndefined })), + t.exact( + t.partial({ + meta, // defaults to undefined if not set during decode + }) + ), ]); export type UpdateListSchemaPartial = Identity>; diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts new file mode 100644 index 00000000000000..15e1c92c06d134 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + _tags, + commentOrUndefined, + created_at, + created_by, + description, + exceptionListItemType, + id, + item_id, + list_id, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; +import { entriesArray } from '../types'; + +// TODO: Should we use a partial here to reflect that this can JSON serialize meta, comment as non existent? +export const exceptionListItemSchema = t.exact( + t.type({ + _tags, + comment: commentOrUndefined, + created_at, + created_by, + description, + entries: entriesArray, + id, + item_id, + list_id, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: exceptionListItemType, + updated_at, + updated_by, + }) +); + +export type ExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts new file mode 100644 index 00000000000000..1940d94597dec6 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + _tags, + created_at, + created_by, + description, + exceptionListType, + id, + list_id, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; + +// TODO: Should we use a partial here to reflect that this can JSON serialize meta as non existent? +export const exceptionListSchema = t.exact( + t.type({ + _tags, + created_at, + created_by, + description, + id, + list_id, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: exceptionListType, + updated_at, + updated_by, + }) +); + +export type ExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts new file mode 100644 index 00000000000000..a58bf433017e66 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { page, per_page, total } from '../common/schemas'; + +import { exceptionListItemSchema } from './exception_list_item_schema'; + +export const foundExceptionListItemSchema = t.exact( + t.type({ + data: t.array(exceptionListItemSchema), + page, + per_page, + total, + }) +); + +export type FoundExceptionListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts new file mode 100644 index 00000000000000..a2ea09a3263ae7 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { page, per_page, total } from '../common/schemas'; + +import { exceptionListSchema } from './exception_list_schema'; + +export const foundExceptionListSchema = t.exact( + t.type({ + data: t.array(exceptionListSchema), + page, + per_page, + total, + }) +); + +export type FoundExceptionListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/index.ts b/x-pack/plugins/lists/common/schemas/response/index.ts index 3f11adf58d8d42..213685d1183bda 100644 --- a/x-pack/plugins/lists/common/schemas/response/index.ts +++ b/x-pack/plugins/lists/common/schemas/response/index.ts @@ -8,3 +8,7 @@ export * from './list_item_schema'; export * from './list_schema'; export * from './acknowledge_schema'; export * from './list_item_index_exist_schema'; +export * from './exception_list_schema'; +export * from './found_exception_list_item_schema'; +export * from './found_exception_list_schema'; +export * from './exception_list_item_schema'; diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts new file mode 100644 index 00000000000000..a08bab65c881c9 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { entriesArrayOrUndefined } from '../types'; +import { + _tags, + commentOrUndefined, + created_at, + created_by, + description, + exceptionListItemType, + exceptionListType, + itemIdOrUndefined, + list_id, + list_type, + metaOrUndefined, + name, + tags, + tie_breaker_id, + updated_by, +} from '../common/schemas'; + +export const exceptionListSoSchema = t.exact( + t.type({ + _tags, + comment: commentOrUndefined, + created_at, + created_by, + description, + entries: entriesArrayOrUndefined, + item_id: itemIdOrUndefined, + list_id, + list_type, + meta: metaOrUndefined, + name, + tags, + tie_breaker_id, + type: t.union([exceptionListType, exceptionListItemType]), + updated_by, + }) +); + +export type ExceptionListSoSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/index.ts b/x-pack/plugins/lists/common/schemas/saved_objects/index.ts new file mode 100644 index 00000000000000..8d42be2d145ca2 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/saved_objects/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './exceptions_list_so_schema'; diff --git a/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts new file mode 100644 index 00000000000000..43698665bb3718 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_entries_array.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +import { EntriesArray, entries } from './entries'; + +export type DefaultEntriesArrayC = t.Type; + +/** + * Types the DefaultEntriesArray as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultEntryArray: DefaultEntriesArrayC = new t.Type< + EntriesArray, + EntriesArray, + unknown +>( + 'DefaultEntryArray', + t.array(entries).is, + (input): Either => + input == null ? t.success([]) : t.array(entries).decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/default_string_array.ts b/x-pack/plugins/lists/common/schemas/types/default_string_array.ts new file mode 100644 index 00000000000000..c6ebffa379903e --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_string_array.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +export type DefaultStringArrayC = t.Type; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default array will be set + */ +export const DefaultStringArray: DefaultStringArrayC = new t.Type( + 'DefaultArray', + t.array(t.string).is, + (input): Either => + input == null ? t.success([]) : t.array(t.string).decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/default_uuid.ts b/x-pack/plugins/lists/common/schemas/types/default_uuid.ts new file mode 100644 index 00000000000000..4fb4133d7353f0 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/default_uuid.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import uuid from 'uuid'; + +import { NonEmptyString } from './non_empty_string'; + +export type DefaultUuidC = t.Type; + +/** + * Types the DefaultUuid as: + * - If null or undefined, then a default string uuid.v4() will be + * created otherwise it will be checked just against an empty string + */ +export const DefaultUuid: DefaultUuidC = new t.Type( + 'DefaultUuid', + t.string.is, + (input): Either => + input == null ? t.success(uuid.v4()) : NonEmptyString.decode(input), + t.identity +); diff --git a/x-pack/plugins/lists/common/schemas/types/entries.ts b/x-pack/plugins/lists/common/schemas/types/entries.ts new file mode 100644 index 00000000000000..750e04e2f39e53 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/entries.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as t from 'io-ts'; + +export const entries = t.exact( + t.type({ + field: t.string, + match: t.union([t.string, t.undefined]), + match_any: t.union([t.array(t.string), t.undefined]), + operator: t.string, // TODO: Use a key of with all possible values + }) +); + +export const entriesArray = t.array(entries); +export type EntriesArray = t.TypeOf; +export type Entries = t.TypeOf; +export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); +export type EntriesArrayOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/index.ts b/x-pack/plugins/lists/common/schemas/types/index.ts new file mode 100644 index 00000000000000..674d7c40c29701 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/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; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './default_entries_array'; +export * from './default_string_array'; +export * from './default_uuid'; +export * from './entries'; +export * from './non_empty_string'; diff --git a/x-pack/plugins/lists/common/siem_common_deps.ts b/x-pack/plugins/lists/common/siem_common_deps.ts index 5e74753a6f0bde..eb4b393a36da33 100644 --- a/x-pack/plugins/lists/common/siem_common_deps.ts +++ b/x-pack/plugins/lists/common/siem_common_deps.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getPaths, foldLeftRight } from '../../siem/server/utils/build_validation/__mocks__/utils'; -export { exactCheck } from '../../siem/server/utils/build_validation/exact_check'; +export { exactCheck } from '../../siem/common/exact_check'; +export { getPaths, foldLeftRight } from '../../siem/common/test_utils'; diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index 5facf981c098e6..ed515757875be8 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -13,7 +13,7 @@ import { SpacesServiceSetup } from '../../spaces/server'; import { ConfigType } from './config'; import { initRoutes } from './routes/init_routes'; -import { ListClient } from './services/lists/client'; +import { ListClient } from './services/lists/list_client'; import { ContextProvider, ContextProviderReturn, @@ -24,6 +24,8 @@ import { import { createConfig$ } from './create_config'; import { getSpaceId } from './get_space_id'; import { getUser } from './get_user'; +import { initSavedObjects } from './saved_objects'; +import { ExceptionListClient } from './services/exception_lists/exception_list_client'; export class ListPlugin implements Plugin, ListsPluginStart, PluginsSetup> { @@ -48,14 +50,22 @@ export class ListPlugin this.config = config; this.security = plugins.security; + initSavedObjects(core.savedObjects); + core.http.registerRouteHandlerContext('lists', this.createRouteHandlerContext()); const router = core.http.createRouter(); initRoutes(router); return { - getListClient: (apiCaller, spaceId, user): ListClient => { + getExceptionListClient: (savedObjectsClient, user): ExceptionListClient => { + return new ExceptionListClient({ + savedObjectsClient, + user, + }); + }, + getListClient: (callCluster, spaceId, user): ListClient => { return new ListClient({ - callCluster: apiCaller, + callCluster, config, spaceId, user, @@ -77,8 +87,9 @@ export class ListPlugin const { spaces, config, security } = this; const { core: { + savedObjects: { client: savedObjectsClient }, elasticsearch: { - dataClient: { callAsCurrentUser }, + dataClient: { callAsCurrentUser: callCluster }, }, }, } = context; @@ -88,9 +99,14 @@ export class ListPlugin const spaceId = getSpaceId({ request, spaces }); const user = getUser({ request, security }); return { + getExceptionListClient: (): ExceptionListClient => + new ExceptionListClient({ + savedObjectsClient, + user, + }), getListClient: (): ListClient => new ListClient({ - callCluster: callAsCurrentUser, + callCluster, config, spaceId, user, diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts new file mode 100644 index 00000000000000..ddcae137a961af --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + CreateExceptionListItemSchemaDecoded, + createExceptionListItemSchema, + exceptionListItemSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils/get_exception_list_client'; + +export const createExceptionListItemRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + body: buildRouteValidation< + typeof createExceptionListItemSchema, + CreateExceptionListItemSchemaDecoded + >(createExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { + name, + _tags, + tags, + meta, + comment, + description, + entries, + item_id: itemId, + list_id: listId, + type, + } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionList = await exceptionLists.getExceptionList({ + id: undefined, + listId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionList == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } else { + const exceptionListItem = await exceptionLists.getExceptionListItem({ + id: undefined, + itemId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionListItem != null) { + return siemResponse.error({ + body: `exception list item id: "${itemId}" already exists`, + statusCode: 409, + }); + } else { + const createdList = await exceptionLists.createExceptionListItem({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + // TODO: Expose the name space type + namespaceType: 'single', + tags, + type, + }); + const [validated, errors] = validate(createdList, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts new file mode 100644 index 00000000000000..c8a1b080c16f66 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + CreateExceptionListSchemaDecoded, + createExceptionListSchema, + exceptionListSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils/get_exception_list_client'; + +export const createExceptionListRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + body: buildRouteValidation< + typeof createExceptionListSchema, + CreateExceptionListSchemaDecoded + >(createExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { name, _tags, tags, meta, description, list_id: listId, type } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionList = await exceptionLists.getExceptionList({ + id: undefined, + listId, + // TODO: Expose the name space type + namespaceType: 'single', + }); + if (exceptionList != null) { + return siemResponse.error({ + body: `exception list id: "${listId}" already exists`, + statusCode: 409, + }); + } else { + const createdList = await exceptionLists.createExceptionList({ + _tags, + description, + listId, + meta, + name, + // TODO: Expose the name space type + namespaceType: 'single', + tags, + type, + }); + const [validated, errors] = validate(createdList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts new file mode 100644 index 00000000000000..e10ffab5359b0e --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteExceptionListItemSchema, exceptionListItemSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; + +export const deleteExceptionListItemRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + query: buildRouteValidation(deleteExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { item_id: itemId, id } = request.query; + if (itemId == null && id == null) { + return siemResponse.error({ + body: 'Either "item_id" or "id" needs to be defined in the request', + statusCode: 400, + }); + } else { + const deleted = await exceptionLists.deleteExceptionListItem({ + id, + itemId, + namespaceType: 'single', // TODO: Bubble this up + }); + if (deleted == null) { + return siemResponse.error({ + body: getErrorMessageExceptionListItem({ id, itemId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts new file mode 100644 index 00000000000000..ef30ab6ab64c5f --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteExceptionListSchema, exceptionListSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; + +export const deleteExceptionListRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + query: buildRouteValidation(deleteExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { list_id: listId, id } = request.query; + if (listId == null && id == null) { + return siemResponse.error({ + body: 'Either "list_id" or "id" needs to be defined in the request', + statusCode: 400, + }); + } else { + // TODO: At the moment this will delete the list but we need to delete all the list items before deleting the list + const deleted = await exceptionLists.deleteExceptionList({ + id, + listId, + namespaceType: 'single', + }); + if (deleted == null) { + return siemResponse.error({ + body: getErrorMessageExceptionList({ id, listId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index 51b4eb9f02cc26..82dfe8a4f29d0c 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -73,7 +73,7 @@ export const deleteListItemRoute = (router: IRouter): void => { } } else { return siemResponse.error({ - body: `Either "list_id" or "id" needs to be defined in the request`, + body: 'Either "list_id" or "id" needs to be defined in the request', statusCode: 400, }); } diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts new file mode 100644 index 00000000000000..3b5503ffb98333 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { findExceptionListItemSchema, foundExceptionListItemSchema } from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const findExceptionListItemRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: `${EXCEPTION_LIST_ITEM_URL}/_find`, + validate: { + query: buildRouteValidation(findExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { + filter, + list_id: listId, + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + } = request.query; + const exceptionListItems = await exceptionLists.findExceptionListItem({ + filter, + listId, + namespaceType: 'single', // TODO: Bubble this up + page, + perPage, + sortField, + sortOrder, + }); + if (exceptionListItems == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } + const [validated, errors] = validate(exceptionListItems, foundExceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts new file mode 100644 index 00000000000000..41c0c0760e03b0 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { findExceptionListSchema, foundExceptionListSchema } from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const findExceptionListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: `${EXCEPTION_LIST_URL}/_find`, + validate: { + query: buildRouteValidation(findExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const exceptionLists = getExceptionListClient(context); + const { + filter, + page, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + } = request.query; + const exceptionListItems = await exceptionLists.findExceptionList({ + filter, + namespaceType: 'single', // TODO: Bubble this up + page, + perPage, + sortField, + sortOrder, + }); + const [validated, errors] = validate(exceptionListItems, foundExceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index 4951cddc56939f..97f497bca71838 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -4,18 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './create_exception_list_item_route'; +export * from './create_exception_list_route'; export * from './create_list_index_route'; export * from './create_list_item_route'; export * from './create_list_route'; +export * from './delete_exception_list_route'; +export * from './delete_exception_list_item_route'; export * from './delete_list_index_route'; export * from './delete_list_item_route'; export * from './delete_list_route'; export * from './export_list_item_route'; +export * from './find_exception_list_item_route'; +export * from './find_exception_list_route'; export * from './import_list_item_route'; export * from './init_routes'; export * from './patch_list_item_route'; export * from './patch_list_route'; +export * from './read_exception_list_item_route'; +export * from './read_exception_list_route'; export * from './read_list_index_route'; export * from './read_list_item_route'; export * from './read_list_route'; +export * from './update_exception_list_item_route'; +export * from './update_exception_list_route'; +export * from './update_list_item_route'; +export * from './update_list_route'; export * from './utils'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 924dd086ee7080..16f96d99505d8a 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -6,23 +6,32 @@ import { IRouter } from 'kibana/server'; -import { updateListRoute } from './update_list_route'; -import { updateListItemRoute } from './update_list_item_route'; - import { + createExceptionListItemRoute, + createExceptionListRoute, createListIndexRoute, createListItemRoute, createListRoute, + deleteExceptionListItemRoute, + deleteExceptionListRoute, deleteListIndexRoute, deleteListItemRoute, deleteListRoute, exportListItemRoute, + findExceptionListItemRoute, + findExceptionListRoute, importListItemRoute, patchListItemRoute, patchListRoute, + readExceptionListItemRoute, + readExceptionListRoute, readListIndexRoute, readListItemRoute, readListRoute, + updateExceptionListItemRoute, + updateExceptionListRoute, + updateListItemRoute, + updateListRoute, } from '.'; export const initRoutes = (router: IRouter): void => { @@ -33,7 +42,7 @@ export const initRoutes = (router: IRouter): void => { deleteListRoute(router); patchListRoute(router); - // lists items + // list items createListItemRoute(router); readListItemRoute(router); updateListItemRoute(router); @@ -46,4 +55,18 @@ export const initRoutes = (router: IRouter): void => { createListIndexRoute(router); readListIndexRoute(router); deleteListIndexRoute(router); + + // exception lists + createExceptionListRoute(router); + readExceptionListRoute(router); + updateExceptionListRoute(router); + deleteExceptionListRoute(router); + findExceptionListRoute(router); + + // exception list items + createExceptionListItemRoute(router); + readExceptionListItemRoute(router); + updateExceptionListItemRoute(router); + deleteExceptionListItemRoute(router); + findExceptionListItemRoute(router); }; diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts new file mode 100644 index 00000000000000..77d37373549c79 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { exceptionListItemSchema, readExceptionListItemSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; + +export const readExceptionListItemRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + query: buildRouteValidation(readExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, item_id: itemId } = request.query; + const exceptionLists = getExceptionListClient(context); + if (id != null || itemId != null) { + const exceptionListItem = await exceptionLists.getExceptionListItem({ + id, + itemId, + // TODO: Bubble this up + namespaceType: 'single', + }); + if (exceptionListItem == null) { + return siemResponse.error({ + body: getErrorMessageExceptionListItem({ id, itemId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionListItem, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else { + return siemResponse.error({ body: 'id or item_id required', statusCode: 400 }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts new file mode 100644 index 00000000000000..1668124acdfce9 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { exceptionListSchema, readExceptionListSchema } from '../../common/schemas'; + +import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; + +export const readExceptionListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + query: buildRouteValidation(readExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId } = request.query; + const exceptionLists = getExceptionListClient(context); + if (id != null || listId != null) { + const exceptionList = await exceptionLists.getExceptionList({ + id, + listId, + // TODO: Bubble this up + namespaceType: 'single', + }); + if (exceptionList == null) { + return siemResponse.error({ + body: getErrorMessageExceptionList({ id, listId }), + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else { + return siemResponse.error({ body: 'id or list_id required', statusCode: 400 }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 0a60cba786f049..10c7f781f554cc 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -77,7 +77,7 @@ export const readListItemRoute = (router: IRouter): void => { } } else { return siemResponse.error({ - body: `Either "list_id" or "id" needs to be defined in the request`, + body: 'Either "list_id" or "id" needs to be defined in the request', statusCode: 400, }); } diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts new file mode 100644 index 00000000000000..478225ee35eb85 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + UpdateExceptionListItemSchemaDecoded, + exceptionListItemSchema, + updateExceptionListItemSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from '.'; + +export const updateExceptionListItemRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_ITEM_URL, + validate: { + body: buildRouteValidation< + typeof updateExceptionListItemSchema, + UpdateExceptionListItemSchemaDecoded + >(updateExceptionListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { + description, + id, + name, + meta, + type, + _tags, + comment, + entries, + item_id: itemId, + tags, + } = request.body; + const exceptionLists = getExceptionListClient(context); + const exceptionListItem = await exceptionLists.updateExceptionListItem({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType: 'single', // TODO: Bubble this up + tags, + type, + }); + if (exceptionListItem == null) { + return siemResponse.error({ + body: `list item id: "${id}" not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(exceptionListItem, exceptionListItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts new file mode 100644 index 00000000000000..a112c7422b952b --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + UpdateExceptionListSchemaDecoded, + exceptionListSchema, + updateExceptionListSchema, +} from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const updateExceptionListRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: EXCEPTION_LIST_URL, + validate: { + body: buildRouteValidation< + typeof updateExceptionListSchema, + UpdateExceptionListSchemaDecoded + >(updateExceptionListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { _tags, tags, name, description, id, list_id: listId, meta, type } = request.body; + const exceptionLists = getExceptionListClient(context); + if (id == null && listId == null) { + return siemResponse.error({ + body: `either id or list_id need to be defined`, + statusCode: 404, + }); + } else { + const list = await exceptionLists.updateExceptionList({ + _tags, + description, + id, + listId, + meta, + name, + namespaceType: 'single', // TODO: Bubble this up + tags, + type, + }); + if (list == null) { + return siemResponse.error({ + body: `exception list id: "${id}" found found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(list, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts new file mode 100644 index 00000000000000..665a7540184a03 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const getErrorMessageExceptionList = ({ + id, + listId, +}: { + id: string | undefined; + listId: string | undefined; +}): string => { + if (id != null) { + return `Exception list id: "${id}" does not exist`; + } else if (listId != null) { + return `Exception list list_id: "${listId}" does not exist`; + } else { + return 'Exception list does not exist'; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts new file mode 100644 index 00000000000000..8e6730ef3db5cd --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const getErrorMessageExceptionListItem = ({ + id, + itemId, +}: { + id: string | undefined; + itemId: string | undefined; +}): string => { + if (id != null) { + return `Exception list item id: "${id}" does not exist`; + } else if (itemId != null) { + return `Exception list item list_id: "${itemId}" does not exist`; + } else { + return 'Exception list item does not exist'; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts new file mode 100644 index 00000000000000..ba01ca617fb8bb --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext } from 'kibana/server'; + +import { ErrorWithStatusCode } from '../../error_with_status_code'; +import { ExceptionListClient } from '../../services/exception_lists/exception_list_client'; + +export const getExceptionListClient = (context: RequestHandlerContext): ExceptionListClient => { + const exceptionLists = context.lists?.getExceptionListClient(); + if (exceptionLists == null) { + throw new ErrorWithStatusCode('Exception lists is not found as a plugin', 404); + } else { + return exceptionLists; + } +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts index a16163ec0fa3a8..6ad69fd994bfdf 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts @@ -6,7 +6,7 @@ import { RequestHandlerContext } from 'kibana/server'; -import { ListClient } from '../../services/lists/client'; +import { ListClient } from '../../services/lists/list_client'; import { ErrorWithStatusCode } from '../../error_with_status_code'; export const getListClient = (context: RequestHandlerContext): ListClient => { diff --git a/x-pack/plugins/lists/server/routes/utils/index.ts b/x-pack/plugins/lists/server/routes/utils/index.ts index a601bdfc003c53..90214872b68e34 100644 --- a/x-pack/plugins/lists/server/routes/utils/index.ts +++ b/x-pack/plugins/lists/server/routes/utils/index.ts @@ -3,4 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +export * from './get_error_message_exception_list_item'; +export * from './get_error_message_exception_list'; export * from './get_list_client'; +export * from './get_exception_list_client'; diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts new file mode 100644 index 00000000000000..9e1a708e0c83b9 --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsType } from 'kibana/server'; + +export const exceptionListSavedObjectType = 'exception-list'; +export const exceptionListAgnosticSavedObjectType = 'exception-list-agnostic'; +export type SavedObjectType = 'exception-list' | 'exception-list-agnostic'; + +/** + * This is a super set of exception list and exception list items. The switch + * to determine if you are using an exception list vs. an exception list item + * is "list_type". If "list_type" is "list" then it is an exception list. If + * "list_type" is "item" then the type is an item. + */ +export const commonMapping: SavedObjectsType['mappings'] = { + properties: { + _tags: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + list_id: { + type: 'keyword', + }, + list_type: { + type: 'keyword', + }, + meta: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + tags: { + type: 'keyword', + }, + tie_breaker_id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + }, +}; + +export const exceptionListMapping: SavedObjectsType['mappings'] = { + properties: { + // There is nothing that is not also used within exceptionListItemMapping + // at this time but if there is it should go here + }, +}; + +export const exceptionListItemMapping: SavedObjectsType['mappings'] = { + properties: { + comment: { + // TODO: investigate what the deep mapping structure of this really is + type: 'keyword', + }, + entries: { + properties: { + field: { + type: 'keyword', + }, + match: { + type: 'keyword', + }, + match_any: { + type: 'keyword', + }, + operator: { + type: 'keyword', + }, + }, + }, + item_id: { + type: 'keyword', + }, + }, +}; + +const combinedMappings: SavedObjectsType['mappings'] = { + properties: { + ...commonMapping.properties, + ...exceptionListMapping.properties, + ...exceptionListItemMapping.properties, + }, +}; + +export const exceptionListType: SavedObjectsType = { + hidden: false, + mappings: combinedMappings, + name: exceptionListSavedObjectType, + namespaceType: 'single', +}; + +export const exceptionListAgnosticType: SavedObjectsType = { + hidden: false, + mappings: combinedMappings, + name: exceptionListAgnosticSavedObjectType, + namespaceType: 'agnostic', +}; diff --git a/x-pack/plugins/lists/server/saved_objects/index.ts b/x-pack/plugins/lists/server/saved_objects/index.ts new file mode 100644 index 00000000000000..b5077ae100cde8 --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './exception_list'; +export * from './init_saved_objects'; diff --git a/x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts b/x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts new file mode 100644 index 00000000000000..aab5ffd0e57fe2 --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/init_saved_objects.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup } from 'kibana/server'; + +import { exceptionListAgnosticType, exceptionListType } from './exception_list'; + +export const initSavedObjects = (savedObjects: CoreSetup['savedObjects']): void => { + savedObjects.registerType(exceptionListAgnosticType); + savedObjects.registerType(exceptionListType); +}; diff --git a/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh b/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh new file mode 100755 index 00000000000000..bb431800c56c33 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh @@ -0,0 +1,33 @@ +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_all_alerts.sh +# https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "exception-list" } + } + }' \ + | jq . + +# Deletes all the agnostic namespace version +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "exception-list-agnostic" } + } + }' \ + | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list.sh new file mode 100755 index 00000000000000..fe2ca501b44163 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh new file mode 100755 index 00000000000000..a87881b3853288 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_by_id.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh new file mode 100755 index 00000000000000..7e09452a23e118 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_item.sh ${item_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh new file mode 100755 index 00000000000000..bbfbc3135ddb86 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_exception_list_item_by_id.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list.sh b/x-pack/plugins/lists/server/scripts/delete_list.sh index 9934ce61c71073..ce9fdd6aa21d42 100755 --- a/x-pack/plugins/lists/server/scripts/delete_list.sh +++ b/x-pack/plugins/lists/server/scripts/delete_list.sh @@ -9,7 +9,7 @@ set -e ./check_env_variables.sh -# Example: ./delete_list_by_list_id.sh ${list_id} +# Example: ./delete_list.sh ${list_id} curl -s -k \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json new file mode 100644 index 00000000000000..520bc4ddf1e094 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json @@ -0,0 +1,8 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "endpoint", + "description": "This is a sample endpoint type exception", + "name": "Sample Endpoint Exception List" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json new file mode 100644 index 00000000000000..8c039bf6788ae8 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json @@ -0,0 +1,5 @@ +{ + "description": "This is a sample exception list with auto created list_id since none was provided", + "name": "Sample Exception List without a whole lot going on", + "type": "endpoint" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json new file mode 100644 index 00000000000000..2b97f37a7fa6b8 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json @@ -0,0 +1,21 @@ +{ + "list_id": "endpoint_list", + "item_id": "endpoint_list_item", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample endpoint type exception", + "name": "Sample Endpoint Exception List", + "entries": [ + { + "field": "actingProcess.file.signer", + "operator": "included", + "match": "Elastic, N.V." + }, + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json new file mode 100644 index 00000000000000..d68a26eb8ffe23 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json @@ -0,0 +1,20 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample endpoint type exception that has no item_id so it creates a new id each time", + "name": "Sample Endpoint Exception List", + "entries": [ + { + "field": "actingProcess.file.signer", + "operator": "included", + "match": "Elastic, N.V." + }, + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json new file mode 100644 index 00000000000000..a7fbe1ea48c026 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json @@ -0,0 +1,8 @@ +{ + "list_id": "endpoint_list", + "_tags": ["endpoint", "process", "malware", "os:linux"], + "tags": ["user added string for a tag", "malware"], + "type": "endpoint", + "description": "Different description", + "name": "Sample Endpoint Exception List" +} diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json new file mode 100644 index 00000000000000..a53079318edfaf --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json @@ -0,0 +1,15 @@ +{ + "item_id": "endpoint_list_item", + "_tags": ["endpoint", "process", "malware", "os:windows"], + "tags": ["user added string for a tag", "malware"], + "type": "simple", + "description": "This is a sample change here this list", + "name": "Sample Endpoint Exception List update change", + "entries": [ + { + "field": "event.category", + "operator": "included", + "match_any": ["process", "malware"] + } + ] +} diff --git a/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh b/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh new file mode 100755 index 00000000000000..85c5b0e518fabf --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +LIST_ID=${1:-endpoint_list} +# Example: ./find_exception_list_items.sh {list-id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items/_find?list_id=${LIST_ID} | jq . diff --git a/x-pack/plugins/lists/server/scripts/find_exception_lists.sh b/x-pack/plugins/lists/server/scripts/find_exception_lists.sh new file mode 100755 index 00000000000000..a1ee184b3e5bbc --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/find_exception_lists.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./find_exception_lists.sh {list-id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/_find | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list.sh b/x-pack/plugins/lists/server/scripts/get_exception_list.sh new file mode 100755 index 00000000000000..34e6de2576879c --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?list_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh new file mode 100755 index 00000000000000..0420a1f7023284 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_by_id.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh new file mode 100755 index 00000000000000..ac8337aab83682 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_item.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?item_id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh new file mode 100755 index 00000000000000..575a529c699069 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_exception_list_item_by_id.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/hard_reset.sh b/x-pack/plugins/lists/server/scripts/hard_reset.sh index 861928866369bc..e00c3a9ace34f8 100755 --- a/x-pack/plugins/lists/server/scripts/hard_reset.sh +++ b/x-pack/plugins/lists/server/scripts/hard_reset.sh @@ -12,3 +12,6 @@ set -e # re-create the list and list item indexes ./delete_list_index.sh ./post_list_index.sh + +# re-create the exception list and exception list items +./delete_all_exception_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list.sh b/x-pack/plugins/lists/server/scripts/post_exception_list.sh new file mode 100755 index 00000000000000..84a775ffcf7f14 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_exception_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/new/exception_list.json}) + +# Example: ./post_exception_list.sh +# Example: ./post_exception_list.sh ./exception_lists/new/exception_list.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/exception_lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh new file mode 100755 index 00000000000000..6cee54b1a6148e --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/new/exception_list_item.json}) + +# Example: ./post_exception_list_item.sh +# Example: ./post_exception_list_item.sh ./exception_lists/new/exception_list_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list.sh b/x-pack/plugins/lists/server/scripts/update_exception_list.sh new file mode 100755 index 00000000000000..d7523a0804a898 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_exception_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/updates/simple_update.json}) + +# Example: ./update_exception_list.sh +# Example: ./update_exception_list.sh ./exception_lists/updates/simple_update.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/exception_lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh b/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh new file mode 100755 index 00000000000000..029bfcdabee3e4 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./exception_lists/updates/simple_update_item.json}) + +# Example: ./update_exception_list_item.sh +# Example: ./update_exception_list_item.sh ./exception_lists/updates/simple_update_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/exception_lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts new file mode 100644 index 00000000000000..7ba832e72bb8e0 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import uuid from 'uuid'; + +import { + Description, + ExceptionListSchema, + ExceptionListSoSchema, + ExceptionListType, + ListId, + MetaOrUndefined, + Name, + Tags, + _Tags, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface CreateExceptionListOptions { + _tags: _Tags; + listId: ListId; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + user: string; + tags: Tags; + tieBreaker?: string; + type: ExceptionListType; +} + +export const createExceptionList = async ({ + _tags, + listId, + savedObjectsClient, + namespaceType, + name, + description, + meta, + user, + tags, + tieBreaker, + type, +}: CreateExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const dateNow = new Date().toISOString(); + const savedObject = await savedObjectsClient.create(savedObjectType, { + _tags, + comment: undefined, + created_at: dateNow, + created_by: user, + description, + entries: undefined, + item_id: undefined, + list_id: listId, + list_type: 'list', + meta, + name, + tags, + tie_breaker_id: tieBreaker ?? uuid.v4(), + type, + updated_by: user, + }); + return transformSavedObjectToExceptionList({ savedObject }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts new file mode 100644 index 00000000000000..4a6dc1da978543 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; +import uuid from 'uuid'; + +import { + CommentOrUndefined, + Description, + EntriesArray, + ExceptionListItemSchema, + ExceptionListSoSchema, + ExceptionListType, + ItemId, + ListId, + MetaOrUndefined, + Name, + Tags, + _Tags, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; + +interface CreateExceptionListItemOptions { + _tags: _Tags; + comment: CommentOrUndefined; + listId: ListId; + itemId: ItemId; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + name: Name; + description: Description; + entries: EntriesArray; + meta: MetaOrUndefined; + user: string; + tags: Tags; + tieBreaker?: string; + type: ExceptionListType; +} + +export const createExceptionListItem = async ({ + _tags, + comment, + entries, + itemId, + listId, + savedObjectsClient, + namespaceType, + name, + description, + meta, + user, + tags, + tieBreaker, + type, +}: CreateExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const dateNow = new Date().toISOString(); + const savedObject = await savedObjectsClient.create(savedObjectType, { + _tags, + comment, + created_at: dateNow, + created_by: user, + description, + entries, + item_id: itemId, + list_id: listId, + list_type: 'item', + meta, + name, + tags, + tie_breaker_id: tieBreaker ?? uuid.v4(), + type, + updated_by: user, + }); + return transformSavedObjectToExceptionListItem({ savedObject }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts new file mode 100644 index 00000000000000..6904438c8d275a --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListSchema, IdOrUndefined, ListIdOrUndefined } from '../../../common/schemas'; + +import { getSavedObjectType } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; +import { deleteExceptionListItemByList } from './delete_exception_list_items_by_list'; + +interface DeleteExceptionListOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionList = async ({ + listId, + id, + namespaceType, + savedObjectsClient, +}: DeleteExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + if (exceptionList == null) { + return null; + } else { + await deleteExceptionListItemByList({ + listId: exceptionList.list_id, + namespaceType, + savedObjectsClient, + }); + await savedObjectsClient.delete(savedObjectType, exceptionList.id); + return exceptionList; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts new file mode 100644 index 00000000000000..3b2d991281cd63 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListItemSchema, IdOrUndefined, ItemIdOrUndefined } from '../../../common/schemas'; + +import { getSavedObjectType } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionListItem } from './get_exception_list_item'; + +interface DeleteExceptionListItemOptions { + itemId: ItemIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionListItem = async ({ + itemId, + id, + namespaceType, + savedObjectsClient, +}: DeleteExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionListItem = await getExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + if (exceptionListItem == null) { + return null; + } else { + await savedObjectsClient.delete(savedObjectType, exceptionListItem.id); + return exceptionListItem; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts new file mode 100644 index 00000000000000..8031f085c16355 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from '../../../../../../src/core/server/'; +import { ListId } from '../../../common/schemas'; + +import { findExceptionListItem } from './find_exception_list_item'; +import { NamespaceType } from './types'; +import { getSavedObjectType } from './utils'; + +const PER_PAGE = 100; + +interface DeleteExceptionListItemByListOptions { + listId: ListId; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; +} + +export const deleteExceptionListItemByList = async ({ + listId, + savedObjectsClient, + namespaceType, +}: DeleteExceptionListItemByListOptions): Promise => { + const ids = await getExceptionListItemIds({ listId, namespaceType, savedObjectsClient }); + await deleteFoundExceptionListItems({ ids, namespaceType, savedObjectsClient }); +}; + +export const getExceptionListItemIds = async ({ + listId, + savedObjectsClient, + namespaceType, +}: DeleteExceptionListItemByListOptions): Promise => { + let page = 1; + let ids: string[] = []; + let foundExceptionListItems = await findExceptionListItem({ + filter: undefined, + listId, + namespaceType, + page, + perPage: PER_PAGE, + savedObjectsClient, + sortField: 'tie_breaker_id', + sortOrder: 'desc', + }); + while (foundExceptionListItems != null && foundExceptionListItems.data.length > 0) { + ids = [...ids, ...foundExceptionListItems.data.map(exceptionListItem => exceptionListItem.id)]; + page += 1; + foundExceptionListItems = await findExceptionListItem({ + filter: undefined, + listId, + namespaceType, + page, + perPage: PER_PAGE, + savedObjectsClient, + sortField: 'tie_breaker_id', + sortOrder: 'desc', + }); + } + return ids; +}; + +/** + * NOTE: This is slow and terrible as we are deleting everything one at a time. + * TODO: Replace this with a bulk call or a delete by query would be more useful + */ +export const deleteFoundExceptionListItems = async ({ + ids, + savedObjectsClient, + namespaceType, +}: { + ids: string[]; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +}): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + ids.forEach(async id => { + try { + await savedObjectsClient.delete(savedObjectType, id); + } catch (err) { + // This can happen from race conditions or networking issues so deleting the id's + // like this is considered "best effort" and it is possible to get dangling pieces + // of data sitting around in which case the user has to manually clean up the data + // I am very hopeful this does not happen often or at all. + } + }); +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts new file mode 100644 index 00000000000000..6e71ed1b3e59d8 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -0,0 +1,250 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { + ExceptionListSchema, + FoundExceptionListItemSchema, + FoundExceptionListSchema, +} from '../../../common/schemas'; + +import { + ConstructorOptions, + CreateExceptionListItemOptions, + CreateExceptionListOptions, + DeleteExceptionListItemOptions, + DeleteExceptionListOptions, + FindExceptionListItemOptions, + FindExceptionListOptions, + GetExceptionListItemOptions, + GetExceptionListOptions, + UpdateExceptionListItemOptions, + UpdateExceptionListOptions, +} from './exception_list_client_types'; +import { getExceptionList } from './get_exception_list'; +import { createExceptionList } from './create_exception_list'; +import { getExceptionListItem } from './get_exception_list_item'; +import { createExceptionListItem } from './create_exception_list_item'; +import { updateExceptionList } from './update_exception_list'; +import { updateExceptionListItem } from './update_exception_list_item'; +import { deleteExceptionList } from './delete_exception_list'; +import { deleteExceptionListItem } from './delete_exception_list_item'; +import { findExceptionListItem } from './find_exception_list_item'; +import { findExceptionList } from './find_exception_list'; + +export class ExceptionListClient { + private readonly user: string; + + private readonly savedObjectsClient: SavedObjectsClientContract; + + constructor({ user, savedObjectsClient }: ConstructorOptions) { + this.user = user; + this.savedObjectsClient = savedObjectsClient; + } + + public getExceptionList = async ({ + listId, + id, + namespaceType, + }: GetExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + }; + + public getExceptionListItem = async ({ + itemId, + id, + namespaceType, + }: GetExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return getExceptionListItem({ id, itemId, namespaceType, savedObjectsClient }); + }; + + public createExceptionList = async ({ + _tags, + description, + listId, + meta, + name, + namespaceType, + tags, + type, + }: CreateExceptionListOptions): Promise => { + const { savedObjectsClient, user } = this; + return createExceptionList({ + _tags, + description, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public updateExceptionList = async ({ + _tags, + id, + description, + listId, + meta, + name, + namespaceType, + tags, + type, + }: UpdateExceptionListOptions): Promise => { + const { savedObjectsClient, user } = this; + return updateExceptionList({ + _tags, + description, + id, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public deleteExceptionList = async ({ + id, + listId, + namespaceType, + }: DeleteExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return deleteExceptionList({ + id, + listId, + namespaceType, + savedObjectsClient, + }); + }; + + public createExceptionListItem = async ({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + namespaceType, + tags, + type, + }: CreateExceptionListItemOptions): Promise => { + const { savedObjectsClient, user } = this; + return createExceptionListItem({ + _tags, + comment, + description, + entries, + itemId, + listId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public updateExceptionListItem = async ({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType, + tags, + type, + }: UpdateExceptionListItemOptions): Promise => { + const { savedObjectsClient, user } = this; + return updateExceptionListItem({ + _tags, + comment, + description, + entries, + id, + itemId, + meta, + name, + namespaceType, + savedObjectsClient, + tags, + type, + user, + }); + }; + + public deleteExceptionListItem = async ({ + id, + itemId, + namespaceType, + }: DeleteExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return deleteExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + }; + + public findExceptionListItem = async ({ + listId, + filter, + perPage, + page, + sortField, + sortOrder, + namespaceType, + }: FindExceptionListItemOptions): Promise => { + const { savedObjectsClient } = this; + return findExceptionListItem({ + filter, + listId, + namespaceType, + page, + perPage, + savedObjectsClient, + sortField, + sortOrder, + }); + }; + + public findExceptionList = async ({ + filter, + perPage, + page, + sortField, + sortOrder, + namespaceType, + }: FindExceptionListOptions): Promise => { + const { savedObjectsClient } = this; + return findExceptionList({ + filter, + namespaceType, + page, + perPage, + savedObjectsClient, + sortField, + sortOrder, + }); + }; +} diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts new file mode 100644 index 00000000000000..cecd6bf3397a73 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { + CommentOrUndefined, + Description, + DescriptionOrUndefined, + EntriesArray, + EntriesArrayOrUndefined, + ExceptionListType, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ItemId, + ItemIdOrUndefined, + ListId, + ListIdOrUndefined, + MetaOrUndefined, + Name, + NameOrUndefined, + Tags, + TagsOrUndefined, + _Tags, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { NamespaceType } from './types'; + +export interface ConstructorOptions { + user: string; + savedObjectsClient: SavedObjectsClientContract; +} + +export interface GetExceptionListOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface CreateExceptionListOptions { + _tags: _Tags; + listId: ListId; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + tags: Tags; + type: ExceptionListType; +} + +export interface UpdateExceptionListOptions { + _tags: _TagsOrUndefined; + id: IdOrUndefined; + listId: ListIdOrUndefined; + namespaceType: NamespaceType; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; + tags: TagsOrUndefined; + type: ExceptionListTypeOrUndefined; +} + +export interface DeleteExceptionListOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + namespaceType: NamespaceType; +} + +export interface DeleteExceptionListItemOptions { + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + namespaceType: NamespaceType; +} + +export interface GetExceptionListItemOptions { + itemId: ItemIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface CreateExceptionListItemOptions { + _tags: _Tags; + comment: CommentOrUndefined; + entries: EntriesArray; + itemId: ItemId; + listId: ListId; + namespaceType: NamespaceType; + name: Name; + description: Description; + meta: MetaOrUndefined; + tags: Tags; + type: ExceptionListType; +} + +export interface UpdateExceptionListItemOptions { + _tags: _TagsOrUndefined; + comment: CommentOrUndefined; + entries: EntriesArrayOrUndefined; + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + namespaceType: NamespaceType; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; + tags: TagsOrUndefined; + type: ExceptionListTypeOrUndefined; +} + +export interface FindExceptionListItemOptions { + listId: ListId; + namespaceType: NamespaceType; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export interface FindExceptionListOptions { + namespaceType: NamespaceType; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts new file mode 100644 index 00000000000000..539dda673208b4 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { ExceptionListSoSchema, FoundExceptionListSchema } from '../../../common/schemas'; +import { SavedObjectType } from '../../saved_objects'; + +import { getSavedObjectType, transformSavedObjectsToFounExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface FindExceptionListOptions { + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export const findExceptionList = async ({ + namespaceType, + savedObjectsClient, + filter, + page, + perPage, + sortField, + sortOrder, +}: FindExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const savedObjectsFindResponse = await savedObjectsClient.find({ + filter: getExceptionListFilter({ filter, savedObjectType }), + page, + perPage, + sortField, + sortOrder, + type: savedObjectType, + }); + return transformSavedObjectsToFounExceptionList({ savedObjectsFindResponse }); +}; + +export const getExceptionListFilter = ({ + filter, + savedObjectType, +}: { + filter: string | undefined; + savedObjectType: SavedObjectType; +}): string => { + if (filter == null) { + return `${savedObjectType}.attributes.list_type: list`; + } else { + return `${savedObjectType}.attributes.list_type: list AND ${filter}`; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts new file mode 100644 index 00000000000000..d635cafbd3b1bd --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { + ExceptionListSoSchema, + FoundExceptionListItemSchema, + ListId, +} from '../../../common/schemas'; +import { SavedObjectType } from '../../saved_objects'; + +import { getSavedObjectType, transformSavedObjectsToFounExceptionListItem } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; + +interface FindExceptionListItemOptions { + listId: ListId; + namespaceType: NamespaceType; + savedObjectsClient: SavedObjectsClientContract; + filter: string | undefined; + perPage: number | undefined; + page: number | undefined; + sortField: string | undefined; + sortOrder: string | undefined; +} + +export const findExceptionListItem = async ({ + listId, + namespaceType, + savedObjectsClient, + filter, + page, + perPage, + sortField, + sortOrder, +}: FindExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ + id: undefined, + listId, + namespaceType, + savedObjectsClient, + }); + if (exceptionList == null) { + return null; + } else { + const savedObjectsFindResponse = await savedObjectsClient.find({ + filter: getExceptionListItemFilter({ filter, listId, savedObjectType }), + page, + perPage, + sortField, + sortOrder, + type: savedObjectType, + }); + return transformSavedObjectsToFounExceptionListItem({ savedObjectsFindResponse }); + } +}; + +export const getExceptionListItemFilter = ({ + filter, + listId, + savedObjectType, +}: { + listId: ListId; + filter: string | undefined; + savedObjectType: SavedObjectType; +}): string => { + if (filter == null) { + return `${savedObjectType}.attributes.list_type: item AND ${savedObjectType}.attributes.list_id: ${listId}`; + } else { + return `${savedObjectType}.attributes.list_type: item AND ${savedObjectType}.attributes.list_id: ${listId} AND ${filter}`; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts new file mode 100644 index 00000000000000..8b28443b4e30ce --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, +} from '../../../../../../src/core/server/'; +import { + ExceptionListSchema, + ExceptionListSoSchema, + IdOrUndefined, + ListIdOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; +import { NamespaceType } from './types'; + +interface GetExceptionListOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export const getExceptionList = async ({ + id, + listId, + savedObjectsClient, + namespaceType, +}: GetExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + if (id != null) { + try { + const savedObject = await savedObjectsClient.get(savedObjectType, id); + return transformSavedObjectToExceptionList({ savedObject }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return null; + } else { + throw err; + } + } + } else if (listId != null) { + const savedObject = await savedObjectsClient.find({ + filter: `${savedObjectType}.attributes.list_type: list`, + perPage: 1, + search: listId, + searchFields: ['list_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); + if (savedObject.saved_objects[0] != null) { + return transformSavedObjectToExceptionList({ savedObject: savedObject.saved_objects[0] }); + } else { + return null; + } + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts new file mode 100644 index 00000000000000..7ef3e4af3d604b --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, +} from '../../../../../../src/core/server/'; +import { + ExceptionListItemSchema, + ExceptionListSoSchema, + IdOrUndefined, + ItemIdOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; + +interface GetExceptionListItemOptions { + id: IdOrUndefined; + itemId: ItemIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export const getExceptionListItem = async ({ + id, + itemId, + savedObjectsClient, + namespaceType, +}: GetExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + if (id != null) { + try { + const savedObject = await savedObjectsClient.get(savedObjectType, id); + return transformSavedObjectToExceptionListItem({ savedObject }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return null; + } else { + throw err; + } + } + } else if (itemId != null) { + const savedObject = await savedObjectsClient.find({ + filter: `${savedObjectType}.attributes.list_type: item`, + perPage: 1, + search: itemId, + searchFields: ['item_id'], + sortField: 'tie_breaker_id', + sortOrder: 'desc', + type: savedObjectType, + }); + if (savedObject.saved_objects[0] != null) { + return transformSavedObjectToExceptionListItem({ + savedObject: savedObject.saved_objects[0], + }); + } else { + return null; + } + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/index.ts b/x-pack/plugins/lists/server/services/exception_lists/index.ts new file mode 100644 index 00000000000000..a66f00819605b0 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './create_exception_list_item'; +export * from './create_exception_list'; +export * from './delete_exception_list_item'; +export * from './delete_exception_list'; +export * from './find_exception_list'; +export * from './find_exception_list_item'; +export * from './get_exception_list_item'; +export * from './get_exception_list'; +export * from './update_exception_list_item'; +export * from './update_exception_list'; diff --git a/x-pack/plugins/lists/server/services/exception_lists/types.ts b/x-pack/plugins/lists/server/services/exception_lists/types.ts new file mode 100644 index 00000000000000..dbb188bc2754a3 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/types.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export type NamespaceType = 'agnostic' | 'single'; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts new file mode 100644 index 00000000000000..6c5ccb5e1f2fd5 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { + DescriptionOrUndefined, + ExceptionListSchema, + ExceptionListSoSchema, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ListIdOrUndefined, + MetaOrUndefined, + NameOrUndefined, + TagsOrUndefined, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectUpdateToExceptionList } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionList } from './get_exception_list'; + +interface UpdateExceptionListOptions { + id: IdOrUndefined; + _tags: _TagsOrUndefined; + name: NameOrUndefined; + description: DescriptionOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + listId: ListIdOrUndefined; + meta: MetaOrUndefined; + user: string; + tags: TagsOrUndefined; + tieBreaker?: string; + type: ExceptionListTypeOrUndefined; +} + +export const updateExceptionList = async ({ + _tags, + id, + savedObjectsClient, + namespaceType, + name, + description, + listId, + meta, + user, + tags, + type, +}: UpdateExceptionListOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionList = await getExceptionList({ id, listId, namespaceType, savedObjectsClient }); + if (exceptionList == null) { + return null; + } else { + const savedObject = await savedObjectsClient.update( + savedObjectType, + exceptionList.id, + { + _tags, + description, + meta, + name, + tags, + type, + updated_by: user, + } + ); + return transformSavedObjectUpdateToExceptionList({ exceptionList, savedObject }); + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts new file mode 100644 index 00000000000000..4e955d4281c4d5 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { + CommentOrUndefined, + DescriptionOrUndefined, + EntriesArrayOrUndefined, + ExceptionListItemSchema, + ExceptionListSoSchema, + ExceptionListTypeOrUndefined, + IdOrUndefined, + ItemIdOrUndefined, + MetaOrUndefined, + NameOrUndefined, + TagsOrUndefined, + _TagsOrUndefined, +} from '../../../common/schemas'; + +import { getSavedObjectType, transformSavedObjectUpdateToExceptionListItem } from './utils'; +import { NamespaceType } from './types'; +import { getExceptionListItem } from './get_exception_list_item'; + +interface UpdateExceptionListItemOptions { + id: IdOrUndefined; + comment: CommentOrUndefined; + _tags: _TagsOrUndefined; + name: NameOrUndefined; + description: DescriptionOrUndefined; + entries: EntriesArrayOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; + itemId: ItemIdOrUndefined; + meta: MetaOrUndefined; + user: string; + tags: TagsOrUndefined; + tieBreaker?: string; + type: ExceptionListTypeOrUndefined; +} + +export const updateExceptionListItem = async ({ + _tags, + comment, + entries, + id, + savedObjectsClient, + namespaceType, + name, + description, + itemId, + meta, + user, + tags, + type, +}: UpdateExceptionListItemOptions): Promise => { + const savedObjectType = getSavedObjectType({ namespaceType }); + const exceptionListItem = await getExceptionListItem({ + id, + itemId, + namespaceType, + savedObjectsClient, + }); + if (exceptionListItem == null) { + return null; + } else { + const savedObject = await savedObjectsClient.update( + savedObjectType, + exceptionListItem.id, + { + _tags, + comment, + description, + entries, + meta, + name, + tags, + type, + updated_by: user, + } + ); + return transformSavedObjectUpdateToExceptionListItem({ exceptionListItem, savedObject }); + } +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts new file mode 100644 index 00000000000000..505ebc43afc5a7 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts @@ -0,0 +1,235 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObject, SavedObjectsFindResponse, SavedObjectsUpdateResponse } from 'kibana/server'; + +import { + ExceptionListItemSchema, + ExceptionListSchema, + ExceptionListSoSchema, + FoundExceptionListItemSchema, + FoundExceptionListSchema, +} from '../../../common/schemas'; +import { + SavedObjectType, + exceptionListAgnosticSavedObjectType, + exceptionListSavedObjectType, +} from '../../saved_objects'; + +import { NamespaceType } from './types'; + +export const getSavedObjectType = ({ + namespaceType, +}: { + namespaceType: NamespaceType; +}): SavedObjectType => { + if (namespaceType === 'agnostic') { + return exceptionListAgnosticSavedObjectType; + } else { + return exceptionListSavedObjectType; + } +}; + +export const transformSavedObjectToExceptionList = ({ + savedObject, +}: { + savedObject: SavedObject; +}): ExceptionListSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + created_at, + created_by, + description, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_by, + }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags, + created_at, + created_by, + description, + id, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_at: updatedAt ?? dateNow, + updated_by, + }; +}; + +export const transformSavedObjectUpdateToExceptionList = ({ + exceptionList, + savedObject, +}: { + exceptionList: ExceptionListSchema; + savedObject: SavedObjectsUpdateResponse; +}): ExceptionListSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { _tags, description, meta, name, tags, type, updated_by: updatedBy }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags: _tags ?? exceptionList._tags, + created_at: exceptionList.created_at, + created_by: exceptionList.created_by, + description: description ?? exceptionList.description, + id, + list_id: exceptionList.list_id, + meta: meta ?? exceptionList.meta, + name: name ?? exceptionList.name, + tags: tags ?? exceptionList.tags, + tie_breaker_id: exceptionList.tie_breaker_id, + type: type ?? exceptionList.type, + updated_at: updatedAt ?? dateNow, + updated_by: updatedBy ?? exceptionList.updated_by, + }; +}; + +export const transformSavedObjectToExceptionListItem = ({ + savedObject, +}: { + savedObject: SavedObject; +}): ExceptionListItemSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + comment, + created_at, + created_by, + description, + entries, + item_id: itemId, + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_by, + }, + id, + updated_at: updatedAt, + } = savedObject; + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: item" + // TODO: Do a throw if item_id or entries is not defined. + return { + _tags, + comment, + created_at, + created_by, + description, + entries: entries ?? [], + id, + item_id: itemId ?? '(unknown)', + list_id, + meta, + name, + tags, + tie_breaker_id, + type, + updated_at: updatedAt ?? dateNow, + updated_by, + }; +}; + +export const transformSavedObjectUpdateToExceptionListItem = ({ + exceptionListItem, + savedObject, +}: { + exceptionListItem: ExceptionListItemSchema; + savedObject: SavedObjectsUpdateResponse; +}): ExceptionListItemSchema => { + const dateNow = new Date().toISOString(); + const { + attributes: { + _tags, + comment, + description, + entries, + meta, + name, + tags, + type, + updated_by: updatedBy, + }, + id, + updated_at: updatedAt, + } = savedObject; + + // TODO: Change this to do a decode and throw if the saved object is not as expected. + // TODO: Do a throw if after the decode this is not the correct "list_type: list" + return { + _tags: _tags ?? exceptionListItem._tags, + comment: comment ?? exceptionListItem.comment, + created_at: exceptionListItem.created_at, + created_by: exceptionListItem.created_by, + description: description ?? exceptionListItem.description, + entries: entries ?? exceptionListItem.entries, + id, + item_id: exceptionListItem.item_id, + list_id: exceptionListItem.list_id, + meta: meta ?? exceptionListItem.meta, + name: name ?? exceptionListItem.name, + tags: tags ?? exceptionListItem.tags, + tie_breaker_id: exceptionListItem.tie_breaker_id, + type: type ?? exceptionListItem.type, + updated_at: updatedAt ?? dateNow, + updated_by: updatedBy ?? exceptionListItem.updated_by, + }; +}; + +export const transformSavedObjectsToFounExceptionListItem = ({ + savedObjectsFindResponse, +}: { + savedObjectsFindResponse: SavedObjectsFindResponse; +}): FoundExceptionListItemSchema => { + return { + data: savedObjectsFindResponse.saved_objects.map(savedObject => + transformSavedObjectToExceptionListItem({ savedObject }) + ), + page: savedObjectsFindResponse.page, + per_page: savedObjectsFindResponse.per_page, + total: savedObjectsFindResponse.total, + }; +}; + +export const transformSavedObjectsToFounExceptionList = ({ + savedObjectsFindResponse, +}: { + savedObjectsFindResponse: SavedObjectsFindResponse; +}): FoundExceptionListSchema => { + return { + data: savedObjectsFindResponse.saved_objects.map(savedObject => + transformSavedObjectToExceptionList({ savedObject }) + ), + page: savedObjectsFindResponse.page, + per_page: savedObjectsFindResponse.per_page, + total: savedObjectsFindResponse.total, + }; +}; diff --git a/x-pack/plugins/lists/server/services/lists/client.ts b/x-pack/plugins/lists/server/services/lists/list_client.ts similarity index 99% rename from x-pack/plugins/lists/server/services/lists/client.ts rename to x-pack/plugins/lists/server/services/lists/list_client.ts index ba22bf72cc18c3..cba48115c746c7 100644 --- a/x-pack/plugins/lists/server/services/lists/client.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client.ts @@ -59,7 +59,7 @@ import { ImportListItemsToStreamOptions, UpdateListItemOptions, UpdateListOptions, -} from './client_types'; +} from './list_client_types'; export class ListClient { private readonly spaceId: string; diff --git a/x-pack/plugins/lists/server/services/lists/client_types.ts b/x-pack/plugins/lists/server/services/lists/list_client_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/client_types.ts rename to x-pack/plugins/lists/server/services/lists/list_client_types.ts diff --git a/x-pack/plugins/lists/server/services/utils/index.ts b/x-pack/plugins/lists/server/services/utils/index.ts index 8a44b5ab607bf5..e6365e689f7616 100644 --- a/x-pack/plugins/lists/server/services/utils/index.ts +++ b/x-pack/plugins/lists/server/services/utils/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export * from './derive_type_from_es_type'; export * from './get_query_filter_from_type_value'; export * from './transform_elastic_to_list_item'; export * from './transform_list_item_to_elastic_query'; -export * from './derive_type_from_es_type'; diff --git a/x-pack/plugins/lists/server/siem_server_deps.ts b/x-pack/plugins/lists/server/siem_server_deps.ts index e78debc8e43492..81ebe9f17b06fe 100644 --- a/x-pack/plugins/lists/server/siem_server_deps.ts +++ b/x-pack/plugins/lists/server/siem_server_deps.ts @@ -6,16 +6,16 @@ export { transformError, + deleteTemplate, + deletePolicy, + deleteAllIndex, + setPolicy, + setTemplate, buildSiemResponse, -} from '../../siem/server/lib/detection_engine/routes/utils'; -export { deleteTemplate } from '../../siem/server/lib/detection_engine/index/delete_template'; -export { deletePolicy } from '../../siem/server/lib/detection_engine/index/delete_policy'; -export { deleteAllIndex } from '../../siem/server/lib/detection_engine/index/delete_all_index'; -export { setPolicy } from '../../siem/server/lib/detection_engine/index/set_policy'; -export { setTemplate } from '../../siem/server/lib/detection_engine/index/set_template'; -export { getTemplateExists } from '../../siem/server/lib/detection_engine/index/get_template_exists'; -export { getPolicyExists } from '../../siem/server/lib/detection_engine/index/get_policy_exists'; -export { createBootstrapIndex } from '../../siem/server/lib/detection_engine/index/create_bootstrap_index'; -export { getIndexExists } from '../../siem/server/lib/detection_engine/index/get_index_exists'; -export { buildRouteValidation } from '../../siem/server/utils/build_validation/route_validation'; -export { validate } from '../../siem/server/lib/detection_engine/routes/rules/validate'; + getTemplateExists, + getPolicyExists, + createBootstrapIndex, + getIndexExists, + buildRouteValidation, + validate, +} from '../../siem/server'; diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/plugins/lists/server/types.ts index d7c3208e556fae..6808aaa04ab7a0 100644 --- a/x-pack/plugins/lists/server/types.ts +++ b/x-pack/plugins/lists/server/types.ts @@ -4,12 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { APICaller, IContextProvider, RequestHandler } from 'kibana/server'; +import { + APICaller, + IContextProvider, + RequestHandler, + SavedObjectsClientContract, +} from 'kibana/server'; import { SecurityPluginSetup } from '../../security/server'; import { SpacesPluginSetup } from '../../spaces/server'; -import { ListClient } from './services/lists/client'; +import { ListClient } from './services/lists/list_client'; +import { ExceptionListClient } from './services/exception_lists/exception_list_client'; export type ContextProvider = IContextProvider, 'lists'>; export type ListsPluginStart = void; @@ -23,14 +29,25 @@ export type GetListClientType = ( spaceId: string, user: string ) => ListClient; + +export type GetExceptionListClientType = ( + savedObjectsClient: SavedObjectsClientContract, + user: string +) => ExceptionListClient; + export interface ListPluginSetup { + getExceptionListClient: GetExceptionListClientType; getListClient: GetListClientType; } -export type ContextProviderReturn = Promise<{ getListClient: () => ListClient }>; +export type ContextProviderReturn = Promise<{ + getListClient: () => ListClient; + getExceptionListClient: () => ExceptionListClient; +}>; declare module 'src/core/server' { interface RequestHandlerContext { lists?: { + getExceptionListClient: () => ExceptionListClient; getListClient: () => ListClient; }; } diff --git a/x-pack/plugins/logstash/public/application/breadcrumbs.js b/x-pack/plugins/logstash/public/application/breadcrumbs.js index 322b9860b37854..4ef259b84e24fb 100644 --- a/x-pack/plugins/logstash/public/application/breadcrumbs.js +++ b/x-pack/plugins/logstash/public/application/breadcrumbs.js @@ -12,7 +12,7 @@ export function getPipelineListBreadcrumbs() { text: i18n.translate('xpack.logstash.pipelines.listBreadcrumb', { defaultMessage: 'Pipelines', }), - href: '#/management/logstash/pipelines', + href: '#/management/ingest/pipelines', }, ]; } diff --git a/x-pack/plugins/logstash/public/plugin.ts b/x-pack/plugins/logstash/public/plugin.ts index 7fbed5b3b86029..7d4afc0a4ea135 100644 --- a/x-pack/plugins/logstash/public/plugin.ts +++ b/x-pack/plugins/logstash/public/plugin.ts @@ -14,8 +14,8 @@ import { HomePublicPluginSetup, FeatureCatalogueCategory, } from '../../../../src/plugins/home/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { LicensingPluginSetup } from '../../licensing/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; // @ts-ignore import { LogstashLicenseService } from './services'; @@ -34,26 +34,23 @@ export class LogstashPlugin implements Plugin { const logstashLicense$ = plugins.licensing.license$.pipe( map(license => new LogstashLicenseService(license)) ); - const section = plugins.management.sections.register({ - id: 'logstash', - title: 'Logstash', - order: 30, - euiIconType: 'logoLogstash', - }); - const managementApp = section.registerApp({ - id: 'pipelines', - title: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { - defaultMessage: 'Pipelines', - }), - order: 10, - mount: async params => { - const [coreStart] = await core.getStartServices(); - const { renderApp } = await import('./application'); - const isMonitoringEnabled = 'monitoring' in plugins; - return renderApp(coreStart, params, isMonitoringEnabled, logstashLicense$); - }, - }); + const managementApp = plugins.management.sections + .getSection(ManagementSectionId.Ingest) + .registerApp({ + id: 'pipelines', + title: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { + defaultMessage: 'Logstash Pipelines', + }), + order: 1, + mount: async params => { + const [coreStart] = await core.getStartServices(); + const { renderApp } = await import('./application'); + const isMonitoringEnabled = 'monitoring' in plugins; + + return renderApp(coreStart, params, isMonitoringEnabled, logstashLicense$); + }, + }); this.licenseSubscription = logstashLicense$.subscribe((license: any) => { if (license.enableLinks) { @@ -74,7 +71,7 @@ export class LogstashPlugin implements Plugin { defaultMessage: 'Create, delete, update, and clone data ingestion pipelines.', }), icon: 'pipelineApp', - path: '/app/kibana#/management/logstash/pipelines', + path: '/app/kibana#/management/ingest/pipelines', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }); diff --git a/x-pack/plugins/maps/common/descriptor_types/descriptor_types.d.ts b/x-pack/plugins/maps/common/descriptor_types/descriptor_types.d.ts index 80e417f9dfa62b..4bdafcabaad06f 100644 --- a/x-pack/plugins/maps/common/descriptor_types/descriptor_types.d.ts +++ b/x-pack/plugins/maps/common/descriptor_types/descriptor_types.d.ts @@ -115,6 +115,7 @@ export type JoinDescriptor = { // todo : this union type is incompatible with dynamic extensibility of sources. // Reconsider using SourceDescriptor in type signatures for top-level classes export type SourceDescriptor = + | AbstractSourceDescriptor | XYZTMSSourceDescriptor | WMSSourceDescriptor | KibanaTilemapSourceDescriptor diff --git a/x-pack/plugins/maps/public/actions/map_actions.js b/x-pack/plugins/maps/public/actions/map_actions.js index 5aa7c74a7b30bd..1dfdfc3a73d8a1 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.js +++ b/x-pack/plugins/maps/public/actions/map_actions.js @@ -111,14 +111,36 @@ function getLayerById(layerId, state) { }); } -async function syncDataForAllLayers(dispatch, getState, dataFilters) { - const state = getState(); - const layerList = getLayerList(state); - const syncs = layerList.map(layer => { - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layer.getId()); - return layer.syncData({ ...loadingFunctions, dataFilters }); - }); - await Promise.all(syncs); +function syncDataForAllLayers() { + return async (dispatch, getState) => { + const syncPromises = getLayerList(getState()).map(async layer => { + return dispatch(syncDataForLayer(layer)); + }); + await Promise.all(syncPromises); + }; +} + +function syncDataForLayer(layer) { + return async (dispatch, getState) => { + const dataFilters = getDataFilters(getState()); + if (!layer.isVisible() || !layer.showAtZoomLevel(dataFilters.zoom)) { + return; + } + + await layer.syncData({ + ...getLayerLoadingCallbacks(dispatch, getState, layer.getId()), + dataFilters, + }); + }; +} + +function syncDataForLayerId(layerId) { + return async (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (layer) { + dispatch(syncDataForLayer(layer)); + } + }; } export function cancelAllInFlightRequests() { @@ -192,7 +214,7 @@ export function rollbackToTrackedLayerStateForSelectedLayer() { // syncDataForLayer may not trigger endDataLoad if no re-fetch is required dispatch(updateStyleMeta(layerId)); - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -252,7 +274,7 @@ export function addLayer(layerDescriptor) { type: ADD_LAYER, layer: layerDescriptor, }); - dispatch(syncDataForLayer(layerDescriptor.id)); + dispatch(syncDataForLayerId(layerDescriptor.id)); }; } @@ -333,7 +355,7 @@ export function setLayerVisibility(layerId, makeVisible) { visibility: makeVisible, }); if (makeVisible) { - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayer(layer)); } }; } @@ -466,8 +488,7 @@ export function mapExtentChanged(newMapConstants) { ...newMapConstants, }, }); - const newDataFilters = { ...dataFilters, ...newMapConstants }; - await syncDataForAllLayers(dispatch, getState, newDataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -751,7 +772,7 @@ export function updateSourceProp(layerId, propName, value, newLayerType) { dispatch(updateLayerType(layerId, newLayerType)); } await dispatch(clearMissingStyleProperties(layerId)); - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -771,20 +792,6 @@ function updateLayerType(layerId, newLayerType) { }; } -export function syncDataForLayer(layerId) { - return async (dispatch, getState) => { - const targetLayer = getLayerById(layerId, getState()); - if (targetLayer) { - const dataFilters = getDataFilters(getState()); - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layerId); - await targetLayer.syncData({ - ...loadingFunctions, - dataFilters, - }); - } - }; -} - export function updateLayerLabel(id, newLabel) { return { type: UPDATE_LAYER_PROP, @@ -830,7 +837,7 @@ export function setLayerQuery(id, query) { newValue: query, }); - dispatch(syncDataForLayer(id)); + dispatch(syncDataForLayerId(id)); }; } @@ -895,8 +902,7 @@ export function setQuery({ query, timeFilters, filters = [], refresh = false }) filters, }); - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -909,13 +915,12 @@ export function setRefreshConfig({ isPaused, interval }) { } export function triggerRefreshTimer() { - return async (dispatch, getState) => { + return async dispatch => { dispatch({ type: TRIGGER_REFRESH_TIMER, }); - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); + await dispatch(syncDataForAllLayers()); }; } @@ -956,7 +961,7 @@ export function updateLayerStyle(layerId, styleDescriptor) { dispatch(updateStyleMeta(layerId)); // Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling - dispatch(syncDataForLayer(layerId)); + dispatch(syncDataForLayerId(layerId)); }; } @@ -994,12 +999,12 @@ export function setJoinsForLayer(layer, joins) { return async dispatch => { await dispatch({ type: SET_JOINS, - layer: layer, - joins: joins, + layer, + joins, }); await dispatch(clearMissingStyleProperties(layer.getId())); - dispatch(syncDataForLayer(layer.getId())); + dispatch(syncDataForLayerId(layer.getId())); }; } diff --git a/x-pack/plugins/maps/public/classes/fields/field.ts b/x-pack/plugins/maps/public/classes/fields/field.ts index 539d0ab4d6ade1..04daedc59c0324 100644 --- a/x-pack/plugins/maps/public/classes/fields/field.ts +++ b/x-pack/plugins/maps/public/classes/fields/field.ts @@ -20,6 +20,7 @@ export interface IField { isValid(): boolean; getOrdinalFieldMetaRequest(): Promise; getCategoricalFieldMetaRequest(size: number): Promise; + supportsFieldMeta(): boolean; } export class AbstractField implements IField { diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js index 69f5033e3af0fa..5108e5cd3e6a3e 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js @@ -25,9 +25,6 @@ export class TileLayer extends AbstractLayer { } async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { - if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) { - return; - } const sourceDataRequest = this.getSourceDataRequest(); if (sourceDataRequest) { //data is immmutable diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index bb4fbe9d01b609..f8f78f6e978298 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -78,10 +78,6 @@ export class TiledVectorLayer extends VectorLayer { } async syncData(syncContext: SyncContext) { - if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { - return; - } - await this._syncSourceStyleMeta(syncContext, this._source, this._style); await this._syncSourceFormatters(syncContext, this._source, this._style); await this._syncMVTUrlTemplate(syncContext); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js index 6c04f7c19ac7db..d32593f73c46cb 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import turf from 'turf'; import React from 'react'; import { AbstractLayer } from '../layer'; import { VectorStyle } from '../../styles/vector/vector_style'; @@ -30,6 +29,7 @@ import { canSkipFormattersUpdate, } from '../../util/can_skip_fetch'; import { assignFeatureIds } from '../../util/assign_feature_ids'; +import { getFeatureCollectionBounds } from '../../util/get_feature_collection_bounds'; import { getFillFilterExpression, getLineFilterExpression, @@ -153,31 +153,10 @@ export class VectorLayer extends AbstractLayer { return this.getCurrentStyle().renderLegendDetails(); } - _getBoundsBasedOnData() { - const featureCollection = this._getSourceFeatureCollection(); - if (!featureCollection) { - return null; - } - - const visibleFeatures = featureCollection.features.filter( - feature => feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] - ); - const bbox = turf.bbox({ - type: 'FeatureCollection', - features: visibleFeatures, - }); - return { - minLon: bbox[0], - minLat: bbox[1], - maxLon: bbox[2], - maxLat: bbox[3], - }; - } - async getBounds(dataFilters) { const isStaticLayer = !this.getSource().isBoundsAware(); if (isStaticLayer) { - return this._getBoundsBasedOnData(); + return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this._hasJoins()); } const searchFilters = this._getSearchFilters( @@ -592,10 +571,6 @@ export class VectorLayer extends AbstractLayer { // Given 2 above, which source/style to use can not be pulled from data request state. // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. async _syncData(syncContext, source, style) { - if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { - return; - } - await this._syncSourceStyleMeta(syncContext, source, style); await this._syncSourceFormatters(syncContext, source, style); const sourceResult = await this._syncSource(syncContext, source, style); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js index fe1ff58922162b..4ffd0d93fd22a0 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js @@ -45,10 +45,6 @@ export class VectorTileLayer extends TileLayer { } async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { - if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) { - return; - } - const nextMeta = { tileLayerId: this.getSource().getTileLayerId() }; const canSkipSync = this._canSkipSync({ prevDataRequest: this.getSourceDataRequest(), diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_icon_property.test.tsx.snap b/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_icon_property.test.tsx.snap new file mode 100644 index 00000000000000..057907353d68ab --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_icon_property.test.tsx.snap @@ -0,0 +1,78 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should render categorical legend with breaks 1`] = ` +
+ + + + + + Other + + } + styleName="icon" + symbolId="square" + /> + + + + + + + + foobar_label + + + + + + +
+`; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts new file mode 100644 index 00000000000000..1c478bb85ccc7e --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/__tests__/test_util.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// eslint-disable-next-line max-classes-per-file +import { FIELD_ORIGIN } from '../../../../../../common/constants'; +import { StyleMeta } from '../../style_meta'; +import { + CategoryFieldMeta, + GeometryTypes, + RangeFieldMeta, + StyleMetaDescriptor, +} from '../../../../../../common/descriptor_types'; +import { AbstractField, IField } from '../../../../fields/field'; + +class MockField extends AbstractField { + async getLabel(): Promise { + return this.getName() + '_label'; + } + supportsFieldMeta(): boolean { + return true; + } +} + +export const mockField: IField = new MockField({ + fieldName: 'foobar', + origin: FIELD_ORIGIN.SOURCE, +}); + +class MockStyle { + getStyleMeta(): StyleMeta { + const geomTypes: GeometryTypes = { + isPointsOnly: false, + isLinesOnly: false, + isPolygonsOnly: false, + }; + const rangeFieldMeta: RangeFieldMeta = { min: 0, max: 100, delta: 100 }; + const catFieldMeta: CategoryFieldMeta = { + categories: [ + { + key: 'US', + count: 10, + }, + { + key: 'CN', + count: 8, + }, + ], + }; + + const styleMetaDescriptor: StyleMetaDescriptor = { + geometryTypes: geomTypes, + fieldMeta: { + foobar: { + range: rangeFieldMeta, + categories: catFieldMeta, + }, + }, + }; + + return new StyleMeta(styleMetaDescriptor); + } +} + +export class MockLayer { + getStyle() { + return new MockStyle(); + } + + getDataRequest() { + return null; + } +} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js index b19c25b369848c..f4c2b8d9260755 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js @@ -15,65 +15,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DynamicColorProperty } from './dynamic_color_property'; -import { StyleMeta } from '../style_meta'; -import { COLOR_MAP_TYPE, FIELD_ORIGIN, VECTOR_STYLES } from '../../../../../common/constants'; - -const mockField = { - async getLabel() { - return 'foobar_label'; - }, - getName() { - return 'foobar'; - }, - getRootName() { - return 'foobar'; - }, - getOrigin() { - return FIELD_ORIGIN.SOURCE; - }, - supportsFieldMeta() { - return true; - }, -}; - -class MockStyle { - getStyleMeta() { - return new StyleMeta({ - geometryTypes: { - isPointsOnly: false, - isLinesOnly: false, - isPolygonsOnly: false, - }, - fieldMeta: { - foobar: { - range: { min: 0, max: 100 }, - categories: { - categories: [ - { - key: 'US', - count: 10, - }, - { - key: 'CN', - count: 8, - }, - ], - }, - }, - }, - }); - } -} - -class MockLayer { - getStyle() { - return new MockStyle(); - } - - getDataRequest() { - return null; - } -} +import { COLOR_MAP_TYPE, VECTOR_STYLES } from '../../../../../common/constants'; +import { mockField, MockLayer } from './__tests__/test_util'; const makeProperty = (options, field = mockField) => { return new DynamicColorProperty(options, VECTOR_STYLES.LINE_COLOR, field, new MockLayer(), () => { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.js index 05e2ad06842cea..27c4fca7d701d5 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.js @@ -29,6 +29,11 @@ export class DynamicIconProperty extends DynamicStyleProperty { return true; } + getNumberOfCategories() { + const palette = getIconPalette(this._options.iconPaletteId); + return palette ? palette.length : 0; + } + syncIconWithMb(symbolLayerId, mbMap, iconPixelSize) { if (this._isIconDynamicConfigComplete()) { mbMap.setLayoutProperty( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx new file mode 100644 index 00000000000000..c2719432130278 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// eslint-disable-next-line max-classes-per-file +import { shallow } from 'enzyme'; + +jest.mock('ui/new_platform'); +jest.mock('../components/vector_style_editor', () => ({ + VectorStyleEditor: () => { + return
mockVectorStyleEditor
; + }, +})); + +import React from 'react'; +import { VECTOR_STYLES } from '../../../../../common/constants'; +// @ts-ignore +import { DynamicIconProperty } from './dynamic_icon_property'; +import { mockField, MockLayer } from './__tests__/test_util'; +import { IconDynamicOptions } from '../../../../../common/descriptor_types'; +import { IField } from '../../../fields/field'; + +const makeProperty = (options: Partial, field: IField = mockField) => { + return new DynamicIconProperty( + { ...options, fieldMetaOptions: { isEnabled: false } }, + VECTOR_STYLES.ICON, + field, + new MockLayer(), + () => { + return (x: string) => x + '_format'; + } + ); +}; + +describe('DynamicIconProperty', () => { + it('should derive category number from palettes', async () => { + const filled = makeProperty({ + iconPaletteId: 'filledShapes', + }); + expect(filled.getNumberOfCategories()).toEqual(6); + const hollow = makeProperty({ + iconPaletteId: 'hollowShapes', + }); + expect(hollow.getNumberOfCategories()).toEqual(5); + }); +}); + +test('Should render categorical legend with breaks', async () => { + const iconStyle = makeProperty({ + iconPaletteId: 'filledShapes', + }); + + const legendRow = iconStyle.renderLegendDetailRow({ isPointsOnly: true, isLinesOnly: false }); + const component = shallow(legendRow); + await new Promise(resolve => process.nextTick(resolve)); + component.update(); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js index 451a79dd3864aa..56a461a3bb147c 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js @@ -148,7 +148,8 @@ export class DynamicStyleProperty extends AbstractStyleProperty { if (this.isOrdinal()) { return this._field.getOrdinalFieldMetaRequest(); } else if (this.isCategorical()) { - return this._field.getCategoricalFieldMetaRequest(this.getNumberOfCategories()); + const numberOfCategories = this.getNumberOfCategories(); + return this._field.getCategoricalFieldMetaRequest(numberOfCategories); } else { return null; } diff --git a/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.test.tsx b/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.test.tsx new file mode 100644 index 00000000000000..ad8b8d37f13107 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getFeatureCollectionBounds } from './get_feature_collection_bounds'; +import { FeatureCollection, Feature, Point } from 'geojson'; +import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../common/constants'; + +const visibleFeature: Feature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [0, 0], + } as Point, + properties: { + [FEATURE_VISIBLE_PROPERTY_NAME]: true, + }, +}; + +const nonVisibleFeature: Feature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [10, 0], + } as Point, + properties: { + [FEATURE_VISIBLE_PROPERTY_NAME]: false, + }, +}; + +const featureWithoutVisibilityProp: Feature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-10, 0], + } as Point, + properties: {}, +}; + +const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [visibleFeature, nonVisibleFeature, featureWithoutVisibilityProp], +}; + +test('should return bounding box for visible features with join', () => { + expect(getFeatureCollectionBounds(featureCollection, true)).toEqual({ + maxLat: 0, + maxLon: 0, + minLat: 0, + minLon: 0, + }); +}); + +test('should return bounding box for all features without join', () => { + expect(getFeatureCollectionBounds(featureCollection, false)).toEqual({ + maxLat: 0, + maxLon: 10, + minLat: 0, + minLon: -10, + }); +}); + +test('should return null when there are no features', () => { + const featureCollectionWithNoVisibileFeatures: FeatureCollection = { + type: 'FeatureCollection', + features: [nonVisibleFeature, featureWithoutVisibilityProp], + }; + expect(getFeatureCollectionBounds(featureCollectionWithNoVisibileFeatures, true)).toBeNull(); +}); diff --git a/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.ts b/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.ts new file mode 100644 index 00000000000000..4247233b295e10 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/util/get_feature_collection_bounds.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// @ts-ignore +import turf from 'turf'; +import { FeatureCollection } from 'geojson'; +import { MapExtent } from '../../../common/descriptor_types'; +import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../common/constants'; + +export function getFeatureCollectionBounds( + featureCollection: FeatureCollection | null, + hasJoins: boolean +): MapExtent | null { + if (!featureCollection) { + return null; + } + + const visibleFeatures = hasJoins + ? featureCollection.features.filter(feature => { + return feature.properties && feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]; + }) + : featureCollection.features; + + if (visibleFeatures.length === 0) { + return null; + } + + const bbox = turf.bbox({ + type: 'FeatureCollection', + features: visibleFeatures, + }); + return { + minLon: bbox[0], + minLat: bbox[1], + maxLon: bbox[2], + maxLat: bbox[3], + }; +} diff --git a/x-pack/plugins/ml/package.json b/x-pack/plugins/ml/package.json index 739dd806fcbb9e..d69d6657fe68c5 100644 --- a/x-pack/plugins/ml/package.json +++ b/x-pack/plugins/ml/package.json @@ -6,7 +6,7 @@ "license": "Elastic-License", "scripts": { "build:apiDocScripts": "cd server/routes/apidoc_scripts && tsc", - "apiDocs": "yarn build:apiDocScripts && cd ./server/routes/ && apidoc --parse-workers apischema=./apidoc_scripts/target/schema_worker.js --parse-parsers apischema=./apidoc_scripts/target/schema_parser.js --parse-filters apiversion=./apidoc_scripts/target/version_filter.js -i . -o ../routes_doc && apidoc-markdown -p ../routes_doc -o ../routes_doc/ML_API.md" + "apiDocs": "yarn build:apiDocScripts && cd ./server/routes/ && apidoc --parse-workers apischema=./apidoc_scripts/target/schema_worker.js --parse-parsers apischema=./apidoc_scripts/target/schema_parser.js --parse-filters apiversion=./apidoc_scripts/target/version_filter.js -i . -o ../routes_doc && apidoc-markdown -p ../routes_doc -o ../routes_doc/ML_API.md -t ./apidoc_scripts/template.md" }, "devDependencies": { "apidoc": "^0.20.1", diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 01cce153ce4944..68e728c019873c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useState } from 'react'; +import React, { Fragment, FC, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; @@ -15,6 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, + EuiSearchBar, } from '@elastic/eui'; import { @@ -51,6 +52,7 @@ import { RefreshAnalyticsListButton } from '../refresh_analytics_list_button'; import { CreateAnalyticsButton } from '../create_analytics_button'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; import { CreateAnalyticsFlyoutWrapper } from '../create_analytics_flyout_wrapper'; +import { getSelectedJobIdFromUrl } from '../../../../../jobs/jobs_list/components/utils'; function getItemIdToExpandedRowMap( itemIds: DataFrameAnalyticsId[], @@ -91,6 +93,8 @@ export const DataFrameAnalyticsList: FC = ({ const [isLoading, setIsLoading] = useState(false); const [filterActive, setFilterActive] = useState(false); + const [queryText, setQueryText] = useState(''); + const [analytics, setAnalytics] = useState([]); const [analyticsStats, setAnalyticsStats] = useState( undefined @@ -107,6 +111,7 @@ export const DataFrameAnalyticsList: FC = ({ const [sortField, setSortField] = useState(DataFrameAnalyticsListColumn.id); const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); + const [jobIdSelected, setJobIdSelected] = useState(false); const disabled = !checkPermission('canCreateDataFrameAnalytics') || !checkPermission('canStartStopDataFrameAnalytics'); @@ -119,6 +124,20 @@ export const DataFrameAnalyticsList: FC = ({ blockRefresh ); + // Query text/job_id based on url but only after getAnalytics is done first + // jobIdSelected makes sure the query is only run once since analytics is being refreshed constantly + const selectedId = getSelectedJobIdFromUrl(window.location.href); + useEffect(() => { + if (jobIdSelected === false && analytics.length > 0) { + if (selectedId !== undefined) { + setJobIdSelected(true); + setQueryText(selectedId); + const selectedIdQuery: Query = EuiSearchBar.Query.parse(selectedId); + onQueryChange({ query: selectedIdQuery, error: undefined }); + } + } + }, [jobIdSelected, analytics]); + // Subscribe to the refresh observable to trigger reloading the analytics list. useRefreshAnalyticsList({ isLoading: setIsLoading, @@ -134,6 +153,7 @@ export const DataFrameAnalyticsList: FC = ({ clauses = query.ast.clauses; } if (clauses.length > 0) { + setQueryText(query.text); setFilterActive(true); filterAnalytics(clauses as Array); } else { @@ -297,6 +317,7 @@ export const DataFrameAnalyticsList: FC = ({ }; const search = { + query: queryText, onChange: onQueryChange, box: { incremental: true, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx index 19b51f76153454..236a8083a95e6c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx @@ -16,8 +16,10 @@ import { EuiScreenReaderOnly, EuiText, EuiToolTip, + EuiLink, RIGHT_ALIGNMENT, } from '@elastic/eui'; +import { getJobIdUrl } from '../../../../../util/get_job_id_url'; import { getAnalysisType, DataFrameAnalyticsId } from '../../../../common'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; @@ -135,6 +137,10 @@ export const progressColumn = { 'data-test-subj': 'mlAnalyticsTableColumnProgress', }; +export const getDFAnalyticsJobIdLink = (item: DataFrameAnalyticsListRow) => ( + {item.id} +); + export const getColumns = ( expandedRowItemIds: DataFrameAnalyticsId[], setExpandedRowItemIds: React.Dispatch>, @@ -193,12 +199,13 @@ export const getColumns = ( 'data-test-subj': 'mlAnalyticsTableRowDetailsToggle', }, { - field: DataFrameAnalyticsListColumn.id, name: 'ID', - sortable: true, + sortable: (item: DataFrameAnalyticsListRow) => item.id, truncateText: true, 'data-test-subj': 'mlAnalyticsTableColumnId', scope: 'row', + render: (item: DataFrameAnalyticsListRow) => + isManagementTable ? getDFAnalyticsJobIdLink(item) : item.id, }, { field: DataFrameAnalyticsListColumn.description, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx index 2d6505f5ce1f75..126fd25a536f6f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx @@ -184,7 +184,7 @@ export const DatavisualizerSelector: FC = () => { footer={ = ({ /> } description="" - href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`} + href={`${basePath.get()}/app/kibana#/management/data/index_management/indices/filter/${index}`} /> diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js index 2a65ee06f2c2c1..307fa79f5dea21 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/create_watch_service.js @@ -167,7 +167,7 @@ class CreateWatchService { saveWatch(watchModel) .then(() => { this.status.watch = this.STATUS.SAVED; - this.config.watcherEditURL = `${basePath.get()}/app/kibana#/management/elasticsearch/watcher/watches/watch/${id}/edit?_g=()`; + this.config.watcherEditURL = `${basePath.get()}/app/kibana#/management/insightsAndAlerting/watcher/watches/watch/${id}/edit?_g=()`; resolve({ id, url: this.config.watcherEditURL, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 7036b4f64b3c52..9874ac56577d32 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -14,7 +14,7 @@ import { toLocaleString } from '../../../../util/string_utils'; import { ResultLinks, actionsMenuContent } from '../job_actions'; import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; -import { getJobIdUrl } from '../utils'; +import { getJobIdUrl } from '../../../../util/get_job_id_url'; import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -71,7 +71,7 @@ export class JobsList extends Component { return id; } - return {id}; + return {id}; } getPageOfJobs(index, size, sortField, sortDirection) { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts new file mode 100644 index 00000000000000..5f72d155cbd5a5 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export function getSelectedJobIdFromUrl(str: string): string; +export function clearSelectedJobIdFromUrl(str: string): void; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 1f2a57f9997758..4f77004b91f999 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -10,7 +10,7 @@ import rison from 'rison-node'; import { mlJobService } from '../../../services/job_service'; import { ml } from '../../../services/ml_api_service'; -import { getToastNotifications, getBasePath } from '../../../util/dependency_cache'; +import { getToastNotifications } from '../../../util/dependency_cache'; import { JOB_STATE, DATAFEED_STATE } from '../../../../../common/constants/states'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { i18n } from '@kbn/i18n'; @@ -367,18 +367,6 @@ function jobProperty(job, prop) { return job[propMap[prop]]; } -export function getJobIdUrl(jobId) { - // Create url for filtering by job id for kibana management table - const settings = { - jobId, - }; - const encoded = rison.encode(settings); - const url = `?mlManagement=${encoded}`; - const basePath = getBasePath(); - - return `${basePath.get()}/app/ml#/jobs${url}`; -} - function getUrlVars(url) { const vars = {}; url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(_, key, value) { diff --git a/x-pack/plugins/ml/public/application/management/index.ts b/x-pack/plugins/ml/public/application/management/index.ts index 6bc5c9b15074fa..f15cdb12afb210 100644 --- a/x-pack/plugins/ml/public/application/management/index.ts +++ b/x-pack/plugins/ml/public/application/management/index.ts @@ -16,7 +16,11 @@ import { take } from 'rxjs/operators'; import { CoreSetup } from 'kibana/public'; import { MlStartDependencies, MlSetupDependencies } from '../../plugin'; -import { PLUGIN_ID, PLUGIN_ICON } from '../../../common/constants/app'; +import { + ManagementAppMountParams, + ManagementSectionId, +} from '../../../../../../src/plugins/management/public'; +import { PLUGIN_ID } from '../../../common/constants/app'; import { MINIMUM_FULL_LICENSE } from '../../../common/license'; export function initManagementSection( @@ -30,22 +34,13 @@ export function initManagementSection( management !== undefined && license.check(PLUGIN_ID, MINIMUM_FULL_LICENSE).state === 'valid' ) { - const mlSection = management.sections.register({ - id: PLUGIN_ID, - title: i18n.translate('xpack.ml.management.mlTitle', { - defaultMessage: 'Machine Learning', - }), - order: 100, - icon: PLUGIN_ICON, - }); - - mlSection.registerApp({ + management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'jobsListLink', title: i18n.translate('xpack.ml.management.jobsListTitle', { - defaultMessage: 'Jobs list', + defaultMessage: 'Machine Learning Jobs', }), - order: 10, - async mount(params) { + order: 2, + async mount(params: ManagementAppMountParams) { const { mountApp } = await import('./jobs_list'); return mountApp(core, params); }, diff --git a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx index 3e4e9cfbd2b663..87a7156b6f52e1 100644 --- a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx @@ -42,7 +42,7 @@ export const OverviewSideBar: FC = ({ createAnomalyDetectionJobDisabled } const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; const docsLink = `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-ml.html`; - const transformsLink = `${basePath.get()}/app/kibana#/management/elasticsearch/transform`; + const transformsLink = `${basePath.get()}/app/kibana#/management/data/transform`; return ( diff --git a/x-pack/plugins/ml/public/application/util/get_job_id_url.ts b/x-pack/plugins/ml/public/application/util/get_job_id_url.ts new file mode 100644 index 00000000000000..a6ca575f21b50b --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/get_job_id_url.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; + * you may not use this file except in compliance with the Elastic License. + */ +import rison from 'rison-node'; + +import { getBasePath } from './dependency_cache'; + +export function getJobIdUrl(tabId: string, jobId: string): string { + // Create url for filtering by job id for kibana management table + const settings = { + jobId, + }; + const encoded = rison.encode(settings); + const url = `?mlManagement=${encoded}`; + const basePath = getBasePath(); + + return `${basePath.get()}/app/ml#/${tabId}${url}`; +} diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index cd311c285d0dfb..c2cb1ad9f0a577 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -1,7 +1,7 @@ { "name": "ml_kibana_api", "version": "7.8.0", - "description": "ML Kibana API", + "description": "This is the documentation of the REST API provided by the Machine Learning Kibana plugin. Each API is experimental and can include breaking changes in any version.", "title": "ML Kibana API", "order": [ "DataFrameAnalytics", diff --git a/x-pack/plugins/ml/server/routes/apidoc_scripts/template.md b/x-pack/plugins/ml/server/routes/apidoc_scripts/template.md new file mode 100644 index 00000000000000..70de461da18d87 --- /dev/null +++ b/x-pack/plugins/ml/server/routes/apidoc_scripts/template.md @@ -0,0 +1,143 @@ +
+# <%= project.name %> v<%= project.version %> + +<%= project.description %> + +<% if (prepend) { -%> +<%- prepend %> +<% } -%> +<% data.forEach(group => { -%> + +## <%= group.name %> +<% group.subs.forEach(sub => { -%> + +### <%= sub.title %> +[Back to top](#top) + +<%- sub.description ? `${sub.description}\n\n` : '' -%> +``` +<%- sub.type.toUpperCase() %> <%= sub.url %> +``` +<% if (sub.header && sub.header.fields && sub.header.fields.Header.length) { -%> + +#### Headers +| Name | Type | Description | +|---------|-----------|--------------------------------------| +<% sub.header.fields.Header.forEach(header => { -%> +| <%- header.field %> | <%- header.type ? `\`${header.type}\`` : '' %> | <%- header.optional ? '**optional**' : '' %><%- header.description %> | +<% }) // foreach parameter -%> +<% } // if parameters -%> +<% if (sub.header && sub.header.examples && sub.header.examples.length) { -%> + +#### Header examples +<% sub.header.examples.forEach(example => { -%> +<%= example.title %> + +``` +<%- example.content %> +``` +<% }) // foreach example -%> +<% } // if example -%> +<% if (sub.parameter && sub.parameter.fields) { -%> +<% Object.keys(sub.parameter.fields).forEach(g => { -%> + +#### Parameters - `<%= g -%>` +| Name | Type | Description | +|:---------|:-----------|:--------------------------------------| +<% sub.parameter.fields[g].forEach(param => { -%> +| <%- param.field -%> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional** ' : '' -%><%- param.description -%> +<% if (param.defaultValue) { -%> +_Default value: <%= param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size %>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach (group) parameter -%> +<% }) // foreach param parameter -%> +<% } // if parameters -%> +<% if (sub.examples && sub.examples.length) { -%> + +#### Examples +<% sub.examples.forEach(example => { -%> +<%= example.title %> + +``` +<%- example.content %> +``` +<% }) // foreach example -%> +<% } // if example -%> +<% if (sub.parameter && sub.parameter.examples && sub.parameter.examples.length) { -%> + +#### Parameters examples +<% sub.parameter.examples.forEach(exampleParam => { -%> +`<%= exampleParam.type %>` - <%= exampleParam.title %> + +```<%= exampleParam.type %> +<%- exampleParam.content %> +``` +<% }) // foreach exampleParam -%> +<% } // if exampleParam -%> +<% if (sub.success && sub.success.fields) { -%> + +#### Success response +<% Object.keys(sub.success.fields).forEach(g => { -%> + +##### Success response - `<%= g %>` +| Name | Type | Description | +|:---------|:-----------|:--------------------------------------| +<% sub.success.fields[g].forEach(param => { -%> +| <%- param.field %> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional**' : '' %><%- param.description -%> +<% if (param.defaultValue) { -%> +_Default value: <%- param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size -%>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach (group) parameter -%> +<% }) // foreach field -%> +<% } // if success.fields -%> +<% if (sub.success && sub.success.examples && sub.success.examples.length) { -%> + +#### Success response example +<% sub.success.examples.forEach(example => { -%> + +##### Success response example - `<%= example.title %>` + +``` +<%- example.content %> +``` +<% }) // foreach success example -%> +<% } // if success.examples -%> +<% if (sub.error && sub.error.fields) { -%> + +#### Error response +<% Object.keys(sub.error.fields).forEach(g => { -%> + +##### Error response - `<%= g %>` +| Name | Type | Description | +|:---------|:-----------|:--------------------------------------| +<% sub.error.fields[g].forEach(param => { -%> +| <%- param.field %> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional**' : '' %><%- param.description -%> +<% if (param.defaultValue) { -%> +_Default value: <%- param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size -%>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach (group) parameter -%> +<% }) // foreach field -%> +<% } // if error.fields -%> +<% if (sub.error && sub.error.examples && sub.error.examples.length) { -%> + +#### Error response example +<% sub.error.examples.forEach(example => { -%> + +##### Error response example - `<%= example.title %>` + +``` +<%- example.content %> +``` +<% }) // foreach error example -%> +<% } // if error.examples -%> +<% }) // foreach sub -%> +<% }) // foreach group -%> diff --git a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index feda891c1ce297..69d7727f9a20a1 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -288,7 +288,7 @@ const handleClickIncompatibleLicense = (scope, clusterName) => { }; const handleClickInvalidLicense = (scope, clusterName) => { - const licensingPath = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`; + const licensingPath = `${Legacy.shims.getBasePath()}/app/kibana#/management/stack/license_management/home`; licenseWarning(scope, { title: toMountPoint( diff --git a/x-pack/plugins/monitoring/public/components/license/index.js b/x-pack/plugins/monitoring/public/components/license/index.js index 085cc9082cf532..e8ea1f8df227ab 100644 --- a/x-pack/plugins/monitoring/public/components/license/index.js +++ b/x-pack/plugins/monitoring/public/components/license/index.js @@ -169,7 +169,7 @@ const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { export function License(props) { const { status, type, isExpired, expiryDate } = props; - const licenseManagement = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management`; + const licenseManagement = `${Legacy.shims.getBasePath()}/app/kibana#/management/stack/license_management`; return ( diff --git a/x-pack/plugins/monitoring/public/types.ts b/x-pack/plugins/monitoring/public/types.ts index 5fcb6b50f5d834..b8c854f4e7ee0d 100644 --- a/x-pack/plugins/monitoring/public/types.ts +++ b/x-pack/plugins/monitoring/public/types.ts @@ -8,6 +8,7 @@ import { PluginInitializerContext, CoreStart } from 'kibana/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { MonitoringConfig } from '../server'; export interface MonitoringPluginDependencies { diff --git a/x-pack/plugins/remote_clusters/public/application/constants/paths.ts b/x-pack/plugins/remote_clusters/public/application/constants/paths.ts index 23fe6758542c9c..770447ce33f936 100644 --- a/x-pack/plugins/remote_clusters/public/application/constants/paths.ts +++ b/x-pack/plugins/remote_clusters/public/application/constants/paths.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CRUD_APP_BASE_PATH: string = '/management/elasticsearch/remote_clusters'; +export const CRUD_APP_BASE_PATH: string = '/management/data/remote_clusters'; diff --git a/x-pack/plugins/remote_clusters/public/plugin.ts b/x-pack/plugins/remote_clusters/public/plugin.ts index 22f98e94748d86..fde8ffa5113196 100644 --- a/x-pack/plugins/remote_clusters/public/plugin.ts +++ b/x-pack/plugins/remote_clusters/public/plugin.ts @@ -6,6 +6,8 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart, PluginInitializerContext } from 'kibana/public'; + +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { init as initBreadcrumbs } from './application/services/breadcrumb'; import { init as initDocumentation } from './application/services/documentation'; import { init as initHttp } from './application/services/http'; @@ -31,13 +33,14 @@ export class RemoteClustersUIPlugin } = this.initializerContext.config.get(); if (isRemoteClustersUiEnabled) { - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.Data); - esSection!.registerApp({ + esSection.registerApp({ id: 'remote_clusters', title: i18n.translate('xpack.remoteClusters.appTitle', { defaultMessage: 'Remote Clusters', }), + order: 7, mount: async ({ element, setBreadcrumbs }) => { const [core] = await getStartServices(); const { diff --git a/x-pack/plugins/reporting/common/types.d.ts b/x-pack/plugins/reporting/common/types.d.ts index 34f0bc9ac8a366..7ab9a15e1773a5 100644 --- a/x-pack/plugins/reporting/common/types.d.ts +++ b/x-pack/plugins/reporting/common/types.d.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths export { ConfigType } from '../server/config'; diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.tsx index 66366cc0b520d5..f600b1ebbb96c0 100644 --- a/x-pack/plugins/reporting/public/plugin.tsx +++ b/x-pack/plugins/reporting/public/plugin.tsx @@ -17,9 +17,9 @@ import { Plugin, PluginInitializerContext, } from 'src/core/public'; -import { ManagementSetup } from 'src/plugins/management/public'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { JobId, JobStatusBuckets } from '../'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public'; import { FeatureCatalogueCategory, @@ -117,10 +117,10 @@ export class ReportingPublicPlugin implements Plugin { category: FeatureCatalogueCategory.ADMIN, }); - management.sections.getSection('kibana')!.registerApp({ + management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'reporting', title: this.title, - order: 15, + order: 1, mount: async params => { const [start] = await getStartServices(); params.setBreadcrumbs([{ text: this.breadcrumbText }]); diff --git a/x-pack/plugins/rollup/public/crud_app/constants/paths.js b/x-pack/plugins/rollup/public/crud_app/constants/paths.js index 83a7ca6bc5967c..44829f38e79cd2 100644 --- a/x-pack/plugins/rollup/public/crud_app/constants/paths.js +++ b/x-pack/plugins/rollup/public/crud_app/constants/paths.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CRUD_APP_BASE_PATH = '/management/elasticsearch/rollup_jobs'; +export const CRUD_APP_BASE_PATH = '/management/data/rollup_jobs'; diff --git a/x-pack/plugins/rollup/public/plugin.ts b/x-pack/plugins/rollup/public/plugin.ts index 0e0333cf30f172..b2e793d7e75e9c 100644 --- a/x-pack/plugins/rollup/public/plugin.ts +++ b/x-pack/plugins/rollup/public/plugin.ts @@ -18,7 +18,7 @@ import { } from '../../../../src/plugins/home/public'; // @ts-ignore import { CRUD_APP_BASE_PATH } from './crud_app/constants'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { IndexManagementPluginSetup } from '../../index_management/public'; import { IndexPatternManagementSetup } from '../../../../src/plugins/index_pattern_management/public'; // @ts-ignore @@ -77,26 +77,23 @@ export class RollupPlugin implements Plugin { }); } - const esSection = management.sections.getSection('elasticsearch'); - if (esSection) { - esSection.registerApp({ - id: 'rollup_jobs', - title: i18n.translate('xpack.rollupJobs.appTitle', { defaultMessage: 'Rollup Jobs' }), - order: 5, - async mount(params) { - params.setBreadcrumbs([ - { - text: i18n.translate('xpack.rollupJobs.breadcrumbsTitle', { - defaultMessage: 'Rollup Jobs', - }), - }, - ]); - const { renderApp } = await import('./application'); + management.sections.getSection(ManagementSectionId.Data).registerApp({ + id: 'rollup_jobs', + title: i18n.translate('xpack.rollupJobs.appTitle', { defaultMessage: 'Rollup Jobs' }), + order: 4, + async mount(params) { + params.setBreadcrumbs([ + { + text: i18n.translate('xpack.rollupJobs.breadcrumbsTitle', { + defaultMessage: 'Rollup Jobs', + }), + }, + ]); + const { renderApp } = await import('./application'); - return renderApp(core, params); - }, - }); - } + return renderApp(core, params); + }, + }); } start(core: CoreStart) { diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts index ee9a1844c7468b..575bc9bf9dff1e 100644 --- a/x-pack/plugins/rollup/server/plugin.ts +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -14,6 +14,7 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { CoreSetup, + ICustomClusterClient, Plugin, Logger, KibanaRequest, @@ -42,11 +43,18 @@ import { mergeCapabilitiesWithFields } from './lib/merge_capabilities_with_field interface RollupContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + // Extend the elasticsearchJs client with additional endpoints. + const esClientConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('rollup', esClientConfig); +} export class RollupPlugin implements Plugin { private readonly logger: Logger; private readonly globalConfig$: Observable; private readonly license: License; + private rollupEsClient?: ICustomClusterClient; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -55,7 +63,7 @@ export class RollupPlugin implements Plugin { } public setup( - { http, uiSettings, elasticsearch }: CoreSetup, + { http, uiSettings, getStartServices }: CoreSetup, { licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies ) { this.license.setup( @@ -72,12 +80,10 @@ export class RollupPlugin implements Plugin { } ); - // Extend the elasticsearchJs client with additional endpoints. - const esClientConfig = { plugins: [elasticsearchJsPlugin] }; - const rollupEsClient = elasticsearch.createClient('rollup', esClientConfig); - http.registerRouteHandlerContext('rollup', (context, request) => { + http.registerRouteHandlerContext('rollup', async (context, request) => { + this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); return { - client: rollupEsClient.asScoped(request), + client: this.rollupEsClient.asScoped(request), }; }); @@ -116,7 +122,12 @@ export class RollupPlugin implements Plugin { const callWithRequestFactoryShim = ( elasticsearchServiceShim: CallWithRequestFactoryShim, request: KibanaRequest - ): APICaller => rollupEsClient.asScoped(request).callAsCurrentUser; + ): APICaller => { + return async (...args: Parameters) => { + this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); + return await this.rollupEsClient.asScoped(request).callAsCurrentUser(...args); + }; + }; const { addSearchStrategy } = visTypeTimeseries; registerRollupSearchStrategy(callWithRequestFactoryShim, addSearchStrategy); @@ -140,5 +151,10 @@ export class RollupPlugin implements Plugin { } start() {} - stop() {} + + stop() { + if (this.rollupEsClient) { + this.rollupEsClient.close(); + } + } } diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx index 460c6550085a45..b59547aff24db3 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx @@ -262,6 +262,7 @@ export class LoginForm extends Component { {this.props.selector.providers.map(provider => ( - ), - pushCallouts: null, - })); + usePostPushToServiceMock.mockImplementation(() => ({ isLoading: false, postPushToService })); + useConnectorsMock.mockImplementation(() => ({ connectors: connectorsMock, isLoading: false })); }); it('should render CaseComponent', async () => { @@ -328,6 +328,7 @@ describe('CaseView ', () => { ...defaultUseGetCaseUserActions, hasDataToPush: true, })); + const wrapper = mount( @@ -335,20 +336,24 @@ describe('CaseView ', () => { ); + + await wait(); + expect( wrapper .find('[data-test-subj="has-data-to-push-button"]') .first() .exists() ).toBeTruthy(); + wrapper - .find('[data-test-subj="mock-button"]') + .find('[data-test-subj="push-to-external-service"]') .first() .simulate('click'); + wrapper.update(); - await wait(); - expect(updateCase).toBeCalledWith(caseProps.caseData); - expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); + + expect(postPushToService).toHaveBeenCalled(); }); it('should return null if error', () => { @@ -429,4 +434,32 @@ describe('CaseView ', () => { expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); expect(fetchCase).toBeCalled(); }); + + it('should disable the push button when connector is invalid', () => { + useGetCaseUserActionsMock.mockImplementation(() => ({ + ...defaultUseGetCaseUserActions, + hasDataToPush: true, + })); + + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find('button[data-test-subj="push-to-external-service"]') + .first() + .prop('disabled') + ).toBeTruthy(); + }); }); diff --git a/x-pack/plugins/siem/public/cases/components/case_view/index.tsx b/x-pack/plugins/siem/public/cases/components/case_view/index.tsx index d02119580a75ab..163272f5087d7a 100644 --- a/x-pack/plugins/siem/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/siem/public/cases/components/case_view/index.tsx @@ -18,6 +18,7 @@ import styled from 'styled-components'; import * as i18n from './translations'; import { Case } from '../../containers/types'; import { getCaseUrl } from '../../../common/components/link_to'; +import { gutterTimeline } from '../../../common/lib/helpers'; import { HeaderPage } from '../../../common/components/header_page'; import { EditableTitle } from '../../../common/components/header_page/editable_title'; import { TagList } from '../tag_list'; @@ -26,9 +27,8 @@ import { UserActionTree } from '../user_action_tree'; import { UserList } from '../user_list'; import { useUpdateCase } from '../../containers/use_update_case'; import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { WrapperPage } from '../../../common/components/wrapper_page'; import { getTypedPayload } from '../../containers/utils'; -import { WhitePageWrapper } from '../wrappers'; +import { WhitePageWrapper, HeaderWrapper } from '../wrappers'; import { useBasePath } from '../../../common/lib/kibana'; import { CaseStatus } from '../case_status'; import { navTabs } from '../../../app/home/home_navigations'; @@ -43,8 +43,11 @@ interface Props { userCanCrud: boolean; } -const MyWrapper = styled(WrapperPage)` - padding-bottom: 0; +const MyWrapper = styled.div` + padding: ${({ + theme, + }) => `${theme.eui.paddingSizes.l} ${gutterTimeline} ${theme.eui.paddingSizes.l} + ${theme.eui.paddingSizes.l}`}; `; const MyEuiFlexGroup = styled(EuiFlexGroup)` @@ -160,10 +163,11 @@ export const CaseComponent = React.memo( ); const { loading: isLoadingConnectors, connectors } = useConnectors(); - const caseConnectorName = useMemo( - () => connectors.find(c => c.id === caseData.connectorId)?.name ?? 'none', - [connectors, caseData.connectorId] - ); + + const [caseConnectorName, isValidConnector] = useMemo(() => { + const connector = connectors.find(c => c.id === caseData.connectorId); + return [connector?.name ?? 'none', !!connector]; + }, [connectors, caseData.connectorId]); const currentExternalIncident = useMemo( () => @@ -182,6 +186,7 @@ export const CaseComponent = React.memo( connectors, updateCase: handleUpdateCase, userCanCrud, + isValidConnector, }); const onSubmitConnector = useCallback( @@ -242,15 +247,20 @@ export const CaseComponent = React.memo( } }, [initLoadingData, isLoadingUserActions]); + const backOptions = useMemo( + () => ({ + href: getCaseUrl(search), + text: i18n.BACK_TO_ALL, + dataTestSubj: 'backToCases', + }), + [search] + ); + return ( <> - + ( {...caseStatusData} /> - + {!initLoadingData && pushCallouts != null && pushCallouts} diff --git a/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.test.tsx b/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.test.tsx index cb002019423127..e3e627e3a136ec 100644 --- a/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.test.tsx +++ b/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.test.tsx @@ -46,7 +46,9 @@ describe('usePushToService', () => { connectors: connectorsMock, updateCase, userCanCrud: true, + isValidConnector: true, }; + beforeEach(() => { jest.resetAllMocks(); (usePostPushToService as jest.Mock).mockImplementation(() => mockPostPush); @@ -55,6 +57,7 @@ describe('usePushToService', () => { actionLicense, })); }); + it('push case button posts the push with correct args', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( @@ -75,6 +78,7 @@ describe('usePushToService', () => { expect(result.current.pushCallouts).toBeNull(); }); }); + it('Displays message when user does not have premium license', async () => { (useGetActionLicense as jest.Mock).mockImplementation(() => ({ isLoading: false, @@ -96,6 +100,7 @@ describe('usePushToService', () => { expect(errorsMsg[0].title).toEqual(getLicenseError().title); }); }); + it('Displays message when user does not have case enabled in config', async () => { (useGetActionLicense as jest.Mock).mockImplementation(() => ({ isLoading: false, @@ -117,6 +122,7 @@ describe('usePushToService', () => { expect(errorsMsg[0].title).toEqual(getKibanaConfigError().title); }); }); + it('Displays message when user does not have a connector configured', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( @@ -135,6 +141,27 @@ describe('usePushToService', () => { expect(errorsMsg[0].title).toEqual(i18n.PUSH_DISABLE_BY_NO_CASE_CONFIG_TITLE); }); }); + + it('Displays message when connector is deleted', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + usePushToService({ + ...defaultArgs, + caseConnectorId: 'not-exist', + isValidConnector: false, + }), + { + wrapper: ({ children }) => {children}, + } + ); + await waitForNextUpdate(); + const errorsMsg = result.current.pushCallouts?.props.messages; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].title).toEqual(i18n.PUSH_DISABLE_BY_NO_CASE_CONFIG_TITLE); + }); + }); + it('Displays message when case is closed', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( diff --git a/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.tsx b/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.tsx index 157639f011fef8..ae8a67b75d36c6 100644 --- a/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.tsx +++ b/x-pack/plugins/siem/public/cases/components/use_push_to_service/index.tsx @@ -29,6 +29,7 @@ export interface UsePushToService { connectors: Connector[]; updateCase: (newCase: Case) => void; userCanCrud: boolean; + isValidConnector: boolean; } export interface ReturnUsePushToService { @@ -45,6 +46,7 @@ export const usePushToService = ({ connectors, updateCase, userCanCrud, + isValidConnector, }: UsePushToService): ReturnUsePushToService => { const urlSearch = useGetUrlSearch(navTabs.case); @@ -77,7 +79,7 @@ export const usePushToService = ({ description: ( @@ -97,7 +99,20 @@ export const usePushToService = ({ description: ( + ), + }, + ]; + } else if (!isValidConnector && !loadingLicense) { + errors = [ + ...errors, + { + title: i18n.PUSH_DISABLE_BY_NO_CASE_CONFIG_TITLE, + description: ( + ), }, @@ -130,7 +145,9 @@ export const usePushToService = ({ fill iconType="importAction" onClick={handlePushToService} - disabled={isLoading || loadingLicense || errorsMsg.length > 0 || !userCanCrud} + disabled={ + isLoading || loadingLicense || errorsMsg.length > 0 || !userCanCrud || !isValidConnector + } isLoading={isLoading} > {caseServices[caseConnectorId] @@ -147,6 +164,7 @@ export const usePushToService = ({ isLoading, loadingLicense, userCanCrud, + isValidConnector, ]); const objToReturn = useMemo(() => { diff --git a/x-pack/plugins/siem/public/cases/components/use_push_to_service/translations.ts b/x-pack/plugins/siem/public/cases/components/use_push_to_service/translations.ts index bdd6ae98a5d01a..4b55aa83ef726a 100644 --- a/x-pack/plugins/siem/public/cases/components/use_push_to_service/translations.ts +++ b/x-pack/plugins/siem/public/cases/components/use_push_to_service/translations.ts @@ -15,9 +15,10 @@ export const ERROR_PUSH_SERVICE_CALLOUT_TITLE = i18n.translate( export const PUSH_THIRD = (thirdParty: string) => { if (thirdParty === 'none') { return i18n.translate('xpack.siem.case.caseView.pushThirdPartyIncident', { - defaultMessage: 'Push as third party incident', + defaultMessage: 'Push as external incident', }); } + return i18n.translate('xpack.siem.case.caseView.pushNamedIncident', { values: { thirdParty }, defaultMessage: 'Push as { thirdParty } incident', @@ -27,9 +28,10 @@ export const PUSH_THIRD = (thirdParty: string) => { export const UPDATE_THIRD = (thirdParty: string) => { if (thirdParty === 'none') { return i18n.translate('xpack.siem.case.caseView.updateThirdPartyIncident', { - defaultMessage: 'Update third party incident', + defaultMessage: 'Update external incident', }); } + return i18n.translate('xpack.siem.case.caseView.updateNamedIncident', { values: { thirdParty }, defaultMessage: 'Update { thirdParty } incident', diff --git a/x-pack/plugins/siem/public/cases/components/wrappers/index.tsx b/x-pack/plugins/siem/public/cases/components/wrappers/index.tsx index 772d78f948b798..06715514e01bf6 100644 --- a/x-pack/plugins/siem/public/cases/components/wrappers/index.tsx +++ b/x-pack/plugins/siem/public/cases/components/wrappers/index.tsx @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; +import { gutterTimeline } from '../../../common/lib/helpers'; export const WhitePageWrapper = styled.div` - ${({ theme }) => css` - background-color: ${theme.eui.euiColorEmptyShade}; - border-top: ${theme.eui.euiBorderThin}; - height: 100%; - min-height: 100vh; - `} + background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; + border-top: ${({ theme }) => theme.eui.euiBorderThin}; + height: 100%; + min-height: 100vh; `; export const SectionWrapper = styled.div` @@ -20,3 +19,8 @@ export const SectionWrapper = styled.div` margin: 0 auto; max-width: 1175px; `; + +export const HeaderWrapper = styled.div` + padding: ${({ theme }) => `${theme.eui.paddingSizes.l} ${gutterTimeline} 0 + ${theme.eui.paddingSizes.l}`}; +`; diff --git a/x-pack/plugins/siem/public/cases/pages/case_details.tsx b/x-pack/plugins/siem/public/cases/pages/case_details.tsx index 5ea5e52951592a..5dfe12179b9900 100644 --- a/x-pack/plugins/siem/public/cases/pages/case_details.tsx +++ b/x-pack/plugins/siem/public/cases/pages/case_details.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { useParams, Redirect } from 'react-router-dom'; +import { WrapperPage } from '../../common/components/wrapper_page'; import { useGetUrlSearch } from '../../common/components/navigation/use_get_url_search'; import { useGetUserSavedObjectPermissions } from '../../common/lib/kibana'; import { SpyRoute } from '../../common/utils/route/spy_routes'; @@ -26,10 +27,15 @@ export const CaseDetailsPage = React.memo(() => { return caseId != null ? ( <> - {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( - - )} - + + {userPermissions != null && !userPermissions?.crud && userPermissions?.read && ( + + )} + + ) : null; diff --git a/x-pack/plugins/siem/public/cases/pages/configure_cases.tsx b/x-pack/plugins/siem/public/cases/pages/configure_cases.tsx index bea3a9fb110ab0..f70ff859e8e7d4 100644 --- a/x-pack/plugins/siem/public/cases/pages/configure_cases.tsx +++ b/x-pack/plugins/siem/public/cases/pages/configure_cases.tsx @@ -6,6 +6,7 @@ import React, { useMemo } from 'react'; import { Redirect } from 'react-router-dom'; +import styled from 'styled-components'; import { getCaseUrl } from '../../common/components/link_to'; import { useGetUrlSearch } from '../../common/components/navigation/use_get_url_search'; @@ -18,12 +19,6 @@ import { ConfigureCases } from '../components/configure_cases'; import { WhitePageWrapper, SectionWrapper } from '../components/wrappers'; import * as i18n from './translations'; -const wrapperPageStyle: Record = { - paddingLeft: '0', - paddingRight: '0', - paddingBottom: '0', -}; - const ConfigureCasesPageComponent: React.FC = () => { const userPermissions = useGetUserSavedObjectPermissions(); const search = useGetUrlSearch(navTabs.case); @@ -40,11 +35,17 @@ const ConfigureCasesPageComponent: React.FC = () => { return ; } + const HeaderWrapper = styled.div` + padding-top: ${({ theme }) => theme.eui.paddingSizes.l}; + `; + return ( <> - + - + + + diff --git a/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap b/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap index 113612200d367c..87cf9fb18adf3e 100644 --- a/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap +++ b/x-pack/plugins/siem/public/common/components/ml_popover/__snapshots__/upgrade_contents.test.tsx.snap @@ -50,7 +50,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` grow={false} > diff --git a/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx b/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx index 5a483e0d5b8760..eda6b0f28499c8 100644 --- a/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx +++ b/x-pack/plugins/siem/public/common/components/ml_popover/upgrade_contents.tsx @@ -59,7 +59,7 @@ export const UpgradeContentsComponent = () => ( diff --git a/x-pack/plugins/siem/public/common/components/wrapper_page/index.tsx b/x-pack/plugins/siem/public/common/components/wrapper_page/index.tsx index bac0357def942d..049d18e59d8bee 100644 --- a/x-pack/plugins/siem/public/common/components/wrapper_page/index.tsx +++ b/x-pack/plugins/siem/public/common/components/wrapper_page/index.tsx @@ -6,26 +6,26 @@ import classNames from 'classnames'; import React from 'react'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import { gutterTimeline } from '../../lib/helpers'; import { AppGlobalStyle } from '../page/index'; -const Wrapper = styled.div` - ${({ theme }) => css` - padding: ${theme.eui.paddingSizes.l} ${gutterTimeline} ${theme.eui.paddingSizes.l} - ${theme.eui.paddingSizes.l}; - - &.siemWrapperPage--restrictWidthDefault, - &.siemWrapperPage--restrictWidthCustom { - box-sizing: content-box; - margin: 0 auto; - } +const Wrapper = styled.div<{ noPadding?: boolean }>` + padding: ${props => + props.noPadding + ? '0' + : `${props.theme.eui.paddingSizes.l} ${gutterTimeline} ${props.theme.eui.paddingSizes.l} + ${props.theme.eui.paddingSizes.l}`}; + &.siemWrapperPage--restrictWidthDefault, + &.siemWrapperPage--restrictWidthCustom { + box-sizing: content-box; + margin: 0 auto; + } - &.siemWrapperPage--restrictWidthDefault { - max-width: 1000px; - } - `} + &.siemWrapperPage--restrictWidthDefault { + max-width: 1000px; + } `; Wrapper.displayName = 'Wrapper'; @@ -35,6 +35,7 @@ interface WrapperPageProps { className?: string; restrictWidth?: boolean | number | string; style?: Record; + noPadding?: boolean; } const WrapperPageComponent: React.FC = ({ @@ -42,6 +43,7 @@ const WrapperPageComponent: React.FC = ({ className, restrictWidth, style, + noPadding, }) => { const classes = classNames(className, { siemWrapperPage: true, @@ -58,7 +60,7 @@ const WrapperPageComponent: React.FC = ({ } return ( - + {children} diff --git a/x-pack/plugins/siem/public/graphql/types.ts b/x-pack/plugins/siem/public/graphql/types.ts index 3436ee84a2f30f..c3493c580fa22e 100644 --- a/x-pack/plugins/siem/public/graphql/types.ts +++ b/x-pack/plugins/siem/public/graphql/types.ts @@ -2541,6 +2541,140 @@ export interface DeleteTimelineMutationArgs { // Documents // ==================================================== +export namespace GetLastEventTimeQuery { + export type Variables = { + sourceId: string; + indexKey: LastEventIndexKey; + details: LastTimeDetails; + defaultIndex: string[]; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + LastEventTime: LastEventTime; + }; + + export type LastEventTime = { + __typename?: 'LastEventTimeData'; + + lastSeen: Maybe; + }; +} + +export namespace GetMatrixHistogramQuery { + export type Variables = { + defaultIndex: string[]; + filterQuery?: Maybe; + histogramType: HistogramType; + inspect: boolean; + sourceId: string; + stackByField: string; + timerange: TimerangeInput; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + MatrixHistogram: MatrixHistogram; + }; + + export type MatrixHistogram = { + __typename?: 'MatrixHistogramOverTimeData'; + + matrixHistogramData: MatrixHistogramData[]; + + totalCount: number; + + inspect: Maybe; + }; + + export type MatrixHistogramData = { + __typename?: 'MatrixOverTimeHistogramData'; + + x: Maybe; + + y: Maybe; + + g: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; + }; +} + +export namespace SourceQuery { + export type Variables = { + sourceId?: Maybe; + defaultIndex: string[]; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + status: Status; + }; + + export type Status = { + __typename?: 'SourceStatus'; + + indicesExist: boolean; + + indexFields: IndexFields[]; + }; + + export type IndexFields = { + __typename?: 'IndexField'; + + category: string; + + description: Maybe; + + example: Maybe; + + indexes: (Maybe)[]; + + name: string; + + searchable: boolean; + + type: string; + + aggregatable: boolean; + + format: Maybe; + }; +} + export namespace GetAuthenticationsQuery { export type Variables = { sourceId: string; @@ -2680,35 +2814,6 @@ export namespace GetAuthenticationsQuery { }; } -export namespace GetLastEventTimeQuery { - export type Variables = { - sourceId: string; - indexKey: LastEventIndexKey; - details: LastTimeDetails; - defaultIndex: string[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - LastEventTime: LastEventTime; - }; - - export type LastEventTime = { - __typename?: 'LastEventTimeData'; - - lastSeen: Maybe; - }; -} - export namespace GetHostFirstLastSeenQuery { export type Variables = { sourceId: string; @@ -2935,11 +3040,11 @@ export namespace GetHostOverviewQuery { }; } -export namespace GetIpOverviewQuery { +export namespace GetKpiHostDetailsQuery { export type Variables = { sourceId: string; + timerange: TimerangeInput; filterQuery?: Maybe; - ip: string; defaultIndex: string[]; inspect: boolean; }; @@ -2955,154 +3060,106 @@ export namespace GetIpOverviewQuery { id: string; - IpOverview: Maybe; + KpiHostDetails: KpiHostDetails; }; - export type IpOverview = { - __typename?: 'IpOverviewData'; + export type KpiHostDetails = { + __typename?: 'KpiHostDetailsData'; - source: Maybe<_Source>; + authSuccess: Maybe; - destination: Maybe; + authSuccessHistogram: Maybe; - host: Host; + authFailure: Maybe; - inspect: Maybe; - }; + authFailureHistogram: Maybe; - export type _Source = { - __typename?: 'Overview'; + uniqueSourceIps: Maybe; - firstSeen: Maybe; + uniqueSourceIpsHistogram: Maybe; - lastSeen: Maybe; + uniqueDestinationIps: Maybe; - autonomousSystem: AutonomousSystem; + uniqueDestinationIpsHistogram: Maybe; - geo: Geo; + inspect: Maybe; }; - export type AutonomousSystem = { - __typename?: 'AutonomousSystem'; + export type AuthSuccessHistogram = KpiHostDetailsChartFields.Fragment; - number: Maybe; + export type AuthFailureHistogram = KpiHostDetailsChartFields.Fragment; - organization: Maybe; - }; + export type UniqueSourceIpsHistogram = KpiHostDetailsChartFields.Fragment; - export type Organization = { - __typename?: 'AutonomousSystemOrganization'; + export type UniqueDestinationIpsHistogram = KpiHostDetailsChartFields.Fragment; - name: Maybe; - }; + export type Inspect = { + __typename?: 'Inspect'; - export type Geo = { - __typename?: 'GeoEcsFields'; + dsl: string[]; - continent_name: Maybe; + response: string[]; + }; +} - city_name: Maybe; +export namespace GetKpiHostsQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; - country_iso_code: Maybe; + export type Query = { + __typename?: 'Query'; - country_name: Maybe; + source: Source; + }; - location: Maybe; + export type Source = { + __typename?: 'Source'; - region_iso_code: Maybe; + id: string; - region_name: Maybe; + KpiHosts: KpiHosts; }; - export type Location = { - __typename?: 'Location'; + export type KpiHosts = { + __typename?: 'KpiHostsData'; - lat: Maybe; + hosts: Maybe; - lon: Maybe; - }; + hostsHistogram: Maybe; - export type Destination = { - __typename?: 'Overview'; + authSuccess: Maybe; - firstSeen: Maybe; + authSuccessHistogram: Maybe; - lastSeen: Maybe; + authFailure: Maybe; - autonomousSystem: _AutonomousSystem; + authFailureHistogram: Maybe; - geo: _Geo; - }; - - export type _AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe<_Organization>; - }; - - export type _Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe<_Location>; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type _Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - architecture: Maybe; - - id: Maybe; - - ip: Maybe; + uniqueSourceIps: Maybe; - mac: Maybe; + uniqueSourceIpsHistogram: Maybe; - name: Maybe; + uniqueDestinationIps: Maybe; - os: Maybe; + uniqueDestinationIpsHistogram: Maybe; - type: Maybe; + inspect: Maybe; }; - export type Os = { - __typename?: 'OsEcsFields'; + export type HostsHistogram = KpiHostChartFields.Fragment; - family: Maybe; + export type AuthSuccessHistogram = KpiHostChartFields.Fragment; - name: Maybe; + export type AuthFailureHistogram = KpiHostChartFields.Fragment; - platform: Maybe; + export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment; - version: Maybe; - }; + export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment; export type Inspect = { __typename?: 'Inspect'; @@ -3113,10 +3170,11 @@ export namespace GetIpOverviewQuery { }; } -export namespace GetKpiHostDetailsQuery { +export namespace GetUncommonProcessesQuery { export type Variables = { sourceId: string; timerange: TimerangeInput; + pagination: PaginationInputPaginated; filterQuery?: Maybe; defaultIndex: string[]; inspect: boolean; @@ -3133,106 +3191,80 @@ export namespace GetKpiHostDetailsQuery { id: string; - KpiHostDetails: KpiHostDetails; + UncommonProcesses: UncommonProcesses; }; - export type KpiHostDetails = { - __typename?: 'KpiHostDetailsData'; - - authSuccess: Maybe; - - authSuccessHistogram: Maybe; - - authFailure: Maybe; - - authFailureHistogram: Maybe; - - uniqueSourceIps: Maybe; + export type UncommonProcesses = { + __typename?: 'UncommonProcessesData'; - uniqueSourceIpsHistogram: Maybe; + totalCount: number; - uniqueDestinationIps: Maybe; + edges: Edges[]; - uniqueDestinationIpsHistogram: Maybe; + pageInfo: PageInfo; inspect: Maybe; }; - export type AuthSuccessHistogram = KpiHostDetailsChartFields.Fragment; - - export type AuthFailureHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueSourceIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueDestinationIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type Inspect = { - __typename?: 'Inspect'; + export type Edges = { + __typename?: 'UncommonProcessesEdges'; - dsl: string[]; + node: Node; - response: string[]; + cursor: Cursor; }; -} -export namespace GetKpiHostsQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; + export type Node = { + __typename?: 'UncommonProcessItem'; - export type Query = { - __typename?: 'Query'; + _id: string; - source: Source; - }; + instances: number; - export type Source = { - __typename?: 'Source'; + process: Process; - id: string; + user: Maybe; - KpiHosts: KpiHosts; + hosts: Hosts[]; }; - export type KpiHosts = { - __typename?: 'KpiHostsData'; - - hosts: Maybe; - - hostsHistogram: Maybe; + export type Process = { + __typename?: 'ProcessEcsFields'; - authSuccess: Maybe; + args: Maybe; - authSuccessHistogram: Maybe; + name: Maybe; + }; - authFailure: Maybe; + export type User = { + __typename?: 'UserEcsFields'; - authFailureHistogram: Maybe; + id: Maybe; - uniqueSourceIps: Maybe; + name: Maybe; + }; - uniqueSourceIpsHistogram: Maybe; + export type Hosts = { + __typename?: 'HostEcsFields'; - uniqueDestinationIps: Maybe; + name: Maybe; + }; - uniqueDestinationIpsHistogram: Maybe; + export type Cursor = { + __typename?: 'CursorType'; - inspect: Maybe; + value: Maybe; }; - export type HostsHistogram = KpiHostChartFields.Fragment; - - export type AuthSuccessHistogram = KpiHostChartFields.Fragment; + export type PageInfo = { + __typename?: 'PageInfoPaginated'; - export type AuthFailureHistogram = KpiHostChartFields.Fragment; + activePage: number; - export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment; + fakeTotalCount: number; - export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment; + showMorePagesIndicator: boolean; + }; export type Inspect = { __typename?: 'Inspect'; @@ -3243,11 +3275,11 @@ export namespace GetKpiHostsQuery { }; } -export namespace GetKpiNetworkQuery { +export namespace GetIpOverviewQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + ip: string; defaultIndex: string[]; inspect: boolean; }; @@ -3263,89 +3295,213 @@ export namespace GetKpiNetworkQuery { id: string; - KpiNetwork: Maybe; + IpOverview: Maybe; }; - export type KpiNetwork = { - __typename?: 'KpiNetworkData'; - - networkEvents: Maybe; - - uniqueFlowId: Maybe; - - uniqueSourcePrivateIps: Maybe; - - uniqueSourcePrivateIpsHistogram: Maybe; - - uniqueDestinationPrivateIps: Maybe; + export type IpOverview = { + __typename?: 'IpOverviewData'; - uniqueDestinationPrivateIpsHistogram: Maybe; + source: Maybe<_Source>; - dnsQueries: Maybe; + destination: Maybe; - tlsHandshakes: Maybe; + host: Host; inspect: Maybe; }; - export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment; + export type _Source = { + __typename?: 'Overview'; - export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment; + firstSeen: Maybe; - export type Inspect = { - __typename?: 'Inspect'; + lastSeen: Maybe; - dsl: string[]; + autonomousSystem: AutonomousSystem; - response: string[]; + geo: Geo; }; -} -export namespace GetMatrixHistogramQuery { - export type Variables = { - defaultIndex: string[]; - filterQuery?: Maybe; - histogramType: HistogramType; - inspect: boolean; - sourceId: string; - stackByField: string; - timerange: TimerangeInput; - }; + export type AutonomousSystem = { + __typename?: 'AutonomousSystem'; - export type Query = { - __typename?: 'Query'; + number: Maybe; - source: Source; + organization: Maybe; }; - export type Source = { - __typename?: 'Source'; - - id: string; + export type Organization = { + __typename?: 'AutonomousSystemOrganization'; - MatrixHistogram: MatrixHistogram; + name: Maybe; }; - export type MatrixHistogram = { - __typename?: 'MatrixHistogramOverTimeData'; + export type Geo = { + __typename?: 'GeoEcsFields'; - matrixHistogramData: MatrixHistogramData[]; + continent_name: Maybe; - totalCount: number; + city_name: Maybe; - inspect: Maybe; - }; + country_iso_code: Maybe; - export type MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; + country_name: Maybe; - x: Maybe; + location: Maybe; - y: Maybe; + region_iso_code: Maybe; - g: Maybe; + region_name: Maybe; + }; + + export type Location = { + __typename?: 'Location'; + + lat: Maybe; + + lon: Maybe; + }; + + export type Destination = { + __typename?: 'Overview'; + + firstSeen: Maybe; + + lastSeen: Maybe; + + autonomousSystem: _AutonomousSystem; + + geo: _Geo; + }; + + export type _AutonomousSystem = { + __typename?: 'AutonomousSystem'; + + number: Maybe; + + organization: Maybe<_Organization>; + }; + + export type _Organization = { + __typename?: 'AutonomousSystemOrganization'; + + name: Maybe; + }; + + export type _Geo = { + __typename?: 'GeoEcsFields'; + + continent_name: Maybe; + + city_name: Maybe; + + country_iso_code: Maybe; + + country_name: Maybe; + + location: Maybe<_Location>; + + region_iso_code: Maybe; + + region_name: Maybe; + }; + + export type _Location = { + __typename?: 'Location'; + + lat: Maybe; + + lon: Maybe; + }; + + export type Host = { + __typename?: 'HostEcsFields'; + + architecture: Maybe; + + id: Maybe; + + ip: Maybe; + + mac: Maybe; + + name: Maybe; + + os: Maybe; + + type: Maybe; + }; + + export type Os = { + __typename?: 'OsEcsFields'; + + family: Maybe; + + name: Maybe; + + platform: Maybe; + + version: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; + }; +} + +export namespace GetKpiNetworkQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + KpiNetwork: Maybe; + }; + + export type KpiNetwork = { + __typename?: 'KpiNetworkData'; + + networkEvents: Maybe; + + uniqueFlowId: Maybe; + + uniqueSourcePrivateIps: Maybe; + + uniqueSourcePrivateIpsHistogram: Maybe; + + uniqueDestinationPrivateIps: Maybe; + + uniqueDestinationPrivateIpsHistogram: Maybe; + + dnsQueries: Maybe; + + tlsHandshakes: Maybe; + + inspect: Maybe; }; + export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment; + + export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment; + export type Inspect = { __typename?: 'Inspect'; @@ -3832,11 +3988,15 @@ export namespace GetNetworkTopNFlowQuery { }; } -export namespace GetOverviewHostQuery { +export namespace GetTlsQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + flowTarget: FlowTargetSourceDest; + ip: string; + pagination: PaginationInputPaginated; + sort: TlsSortField; + timerange: TimerangeInput; defaultIndex: string[]; inspect: boolean; }; @@ -3852,45 +4012,57 @@ export namespace GetOverviewHostQuery { id: string; - OverviewHost: Maybe; + Tls: Tls; }; - export type OverviewHost = { - __typename?: 'OverviewHostData'; + export type Tls = { + __typename?: 'TlsData'; - auditbeatAuditd: Maybe; + totalCount: number; - auditbeatFIM: Maybe; + edges: Edges[]; - auditbeatLogin: Maybe; + pageInfo: PageInfo; - auditbeatPackage: Maybe; + inspect: Maybe; + }; - auditbeatProcess: Maybe; + export type Edges = { + __typename?: 'TlsEdges'; - auditbeatUser: Maybe; + node: Node; - endgameDns: Maybe; + cursor: Cursor; + }; - endgameFile: Maybe; + export type Node = { + __typename?: 'TlsNode'; - endgameImageLoad: Maybe; + _id: Maybe; - endgameNetwork: Maybe; + subjects: Maybe; - endgameProcess: Maybe; + ja3: Maybe; - endgameRegistry: Maybe; + issuers: Maybe; - endgameSecurity: Maybe; + notAfter: Maybe; + }; - filebeatSystemModule: Maybe; + export type Cursor = { + __typename?: 'CursorType'; - winlogbeatSecurity: Maybe; + value: Maybe; + }; - winlogbeatMWSysmonOperational: Maybe; + export type PageInfo = { + __typename?: 'PageInfoPaginated'; - inspect: Maybe; + activePage: number; + + fakeTotalCount: number; + + showMorePagesIndicator: boolean; }; export type Inspect = { @@ -3902,11 +4074,15 @@ export namespace GetOverviewHostQuery { }; } -export namespace GetOverviewNetworkQuery { +export namespace GetUsersQuery { export type Variables = { sourceId: string; - timerange: TimerangeInput; filterQuery?: Maybe; + flowTarget: FlowTarget; + ip: string; + pagination: PaginationInputPaginated; + sort: UsersSortField; + timerange: TimerangeInput; defaultIndex: string[]; inspect: boolean; }; @@ -3922,35 +4098,67 @@ export namespace GetOverviewNetworkQuery { id: string; - OverviewNetwork: Maybe; + Users: Users; }; - export type OverviewNetwork = { - __typename?: 'OverviewNetworkData'; - - auditbeatSocket: Maybe; + export type Users = { + __typename?: 'UsersData'; - filebeatCisco: Maybe; + totalCount: number; - filebeatNetflow: Maybe; + edges: Edges[]; - filebeatPanw: Maybe; + pageInfo: PageInfo; - filebeatSuricata: Maybe; + inspect: Maybe; + }; - filebeatZeek: Maybe; + export type Edges = { + __typename?: 'UsersEdges'; - packetbeatDNS: Maybe; + node: Node; - packetbeatFlow: Maybe; + cursor: Cursor; + }; - packetbeatTLS: Maybe; + export type Node = { + __typename?: 'UsersNode'; - inspect: Maybe; + user: Maybe; }; - export type Inspect = { - __typename?: 'Inspect'; + export type User = { + __typename?: 'UsersItem'; + + name: Maybe; + + id: Maybe; + + groupId: Maybe; + + groupName: Maybe; + + count: Maybe; + }; + + export type Cursor = { + __typename?: 'CursorType'; + + value: Maybe; + }; + + export type PageInfo = { + __typename?: 'PageInfoPaginated'; + + activePage: number; + + fakeTotalCount: number; + + showMorePagesIndicator: boolean; + }; + + export type Inspect = { + __typename?: 'Inspect'; dsl: string[]; @@ -3958,10 +4166,13 @@ export namespace GetOverviewNetworkQuery { }; } -export namespace SourceQuery { +export namespace GetOverviewHostQuery { export type Variables = { - sourceId?: Maybe; + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; defaultIndex: string[]; + inspect: boolean; }; export type Query = { @@ -3975,37 +4186,109 @@ export namespace SourceQuery { id: string; - status: Status; + OverviewHost: Maybe; }; - export type Status = { - __typename?: 'SourceStatus'; + export type OverviewHost = { + __typename?: 'OverviewHostData'; - indicesExist: boolean; + auditbeatAuditd: Maybe; - indexFields: IndexFields[]; + auditbeatFIM: Maybe; + + auditbeatLogin: Maybe; + + auditbeatPackage: Maybe; + + auditbeatProcess: Maybe; + + auditbeatUser: Maybe; + + endgameDns: Maybe; + + endgameFile: Maybe; + + endgameImageLoad: Maybe; + + endgameNetwork: Maybe; + + endgameProcess: Maybe; + + endgameRegistry: Maybe; + + endgameSecurity: Maybe; + + filebeatSystemModule: Maybe; + + winlogbeatSecurity: Maybe; + + winlogbeatMWSysmonOperational: Maybe; + + inspect: Maybe; }; - export type IndexFields = { - __typename?: 'IndexField'; + export type Inspect = { + __typename?: 'Inspect'; - category: string; + dsl: string[]; - description: Maybe; + response: string[]; + }; +} - example: Maybe; +export namespace GetOverviewNetworkQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + }; - indexes: (Maybe)[]; + export type Query = { + __typename?: 'Query'; - name: string; + source: Source; + }; - searchable: boolean; + export type Source = { + __typename?: 'Source'; - type: string; + id: string; - aggregatable: boolean; + OverviewNetwork: Maybe; + }; - format: Maybe; + export type OverviewNetwork = { + __typename?: 'OverviewNetworkData'; + + auditbeatSocket: Maybe; + + filebeatCisco: Maybe; + + filebeatNetflow: Maybe; + + filebeatPanw: Maybe; + + filebeatSuricata: Maybe; + + filebeatZeek: Maybe; + + packetbeatDNS: Maybe; + + packetbeatFlow: Maybe; + + packetbeatTLS: Maybe; + + inspect: Maybe; + }; + + export type Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; }; } @@ -5662,289 +5945,6 @@ export namespace PersistTimelinePinnedEventMutation { }; } -export namespace GetTlsQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTargetSourceDest; - ip: string; - pagination: PaginationInputPaginated; - sort: TlsSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Tls: Tls; - }; - - export type Tls = { - __typename?: 'TlsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'TlsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'TlsNode'; - - _id: Maybe; - - subjects: Maybe; - - ja3: Maybe; - - issuers: Maybe; - - notAfter: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUncommonProcessesQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - UncommonProcesses: UncommonProcesses; - }; - - export type UncommonProcesses = { - __typename?: 'UncommonProcessesData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UncommonProcessesEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UncommonProcessItem'; - - _id: string; - - instances: number; - - process: Process; - - user: Maybe; - - hosts: Hosts[]; - }; - - export type Process = { - __typename?: 'ProcessEcsFields'; - - args: Maybe; - - name: Maybe; - }; - - export type User = { - __typename?: 'UserEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type Hosts = { - __typename?: 'HostEcsFields'; - - name: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUsersQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTarget; - ip: string; - pagination: PaginationInputPaginated; - sort: UsersSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Users: Users; - }; - - export type Users = { - __typename?: 'UsersData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UsersEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UsersNode'; - - user: Maybe; - }; - - export type User = { - __typename?: 'UsersItem'; - - name: Maybe; - - id: Maybe; - - groupId: Maybe; - - groupName: Maybe; - - count: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace KpiHostDetailsChartFields { export type Fragment = { __typename?: 'KpiHostHistogramData'; diff --git a/x-pack/plugins/siem/scripts/generate_types_from_graphql.js b/x-pack/plugins/siem/scripts/generate_types_from_graphql.js index e6b063dfd2c071..21a37e31c6e809 100644 --- a/x-pack/plugins/siem/scripts/generate_types_from_graphql.js +++ b/x-pack/plugins/siem/scripts/generate_types_from_graphql.js @@ -11,7 +11,7 @@ const { join, resolve } = require('path'); const { generate } = require('graphql-code-generator'); const GRAPHQL_GLOBS = [ - join('public', 'containers', '**', '*.gql_query.ts{,x}'), + join('public', '**', '*.gql_query.ts{,x}'), join('common', 'graphql', '**', '*.gql_query.ts{,x}'), ]; const OUTPUT_INTROSPECTION_PATH = resolve('public', 'graphql', 'introspection.json'); diff --git a/x-pack/plugins/siem/server/index.ts b/x-pack/plugins/siem/server/index.ts index e9cd78589fac91..586b9dec2f4ab1 100644 --- a/x-pack/plugins/siem/server/index.ts +++ b/x-pack/plugins/siem/server/index.ts @@ -15,3 +15,17 @@ export const plugin = (context: PluginInitializerContext) => { export const config = { schema: configSchema }; export { ConfigType, Plugin, PluginSetup, PluginStart }; + +// Exports to be shared with plugins such as x-pack/lists plugin +export { deleteTemplate } from './lib/detection_engine/index/delete_template'; +export { deletePolicy } from './lib/detection_engine/index/delete_policy'; +export { deleteAllIndex } from './lib/detection_engine/index/delete_all_index'; +export { setPolicy } from './lib/detection_engine/index/set_policy'; +export { setTemplate } from './lib/detection_engine/index/set_template'; +export { getTemplateExists } from './lib/detection_engine/index/get_template_exists'; +export { getPolicyExists } from './lib/detection_engine/index/get_policy_exists'; +export { createBootstrapIndex } from './lib/detection_engine/index/create_bootstrap_index'; +export { getIndexExists } from './lib/detection_engine/index/get_index_exists'; +export { buildRouteValidation } from './utils/build_validation/route_validation'; +export { validate } from './lib/detection_engine/routes/rules/validate'; +export { transformError, buildSiemResponse } from './lib/detection_engine/routes/utils'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index cfba40fc225a24..cda3a4b81ed9b1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -9,8 +9,8 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; -import { formatErrors } from '../../../../utils/build_validation/format_errors'; -import { exactCheck } from '../../../../utils/build_validation/exact_check'; +import { formatErrors } from '../../../../../common/format_errors'; +import { exactCheck } from '../../../../../common/exact_check'; import { PartialAlert, FindResult } from '../../../../../../alerting/server'; import { isAlertType, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts index fbd2382e2826d4..0b0d3bf43b1e9c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts @@ -20,8 +20,8 @@ import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; import { TypeAndTimelineOnly } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('check_type_dependents', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts index 6e159a792edb66..9bbde3d5236db5 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts @@ -10,8 +10,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { getErrorPayload } from './__mocks__/utils'; import { errorSchema, ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('error_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts index 68b67db595d76d..1b7d7994462c75 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts @@ -10,8 +10,8 @@ import { getFindResponseSingle, getBaseResponsePayload } from './__mocks__/utils import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../../../../common/exact_check'; describe('find_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts index b0b863ebbbc0b2..18e17a319883ac 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts @@ -9,9 +9,9 @@ import { left, Either } from 'fp-ts/lib/Either'; import { ImportRulesSchema, importRulesSchema } from './import_rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; import { Errors } from 'io-ts'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('import_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts index 827167c63fd58e..2d3fd759148220 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts @@ -8,8 +8,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; import { PrePackagedRulesSchema, prePackagedRulesSchema } from './prepackaged_rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts index a864667583c0a8..abe601a546111b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts @@ -11,8 +11,8 @@ import { prePackagedRulesStatusSchema, } from './prepackaged_rules_status_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts index 9a7cf5e2c2871c..98cb2ef058485c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts @@ -12,8 +12,8 @@ import { RulesBulkSchema, rulesBulkSchema } from './rules_bulk_schema'; import { RulesSchema } from './rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts index 82a6682e6461f9..ade4d12517aca2 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts @@ -10,8 +10,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { rulesSchema, RulesSchema, removeList } from './rules_schema'; import { getBaseResponsePayload } from './__mocks__/utils'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; +import { exactCheck } from '../../../../../../common/exact_check'; export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts index 85fb124464487a..8f06e2c6e49b0c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts @@ -9,8 +9,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { TypeAndTimelineOnly, typeAndTimelineOnlySchema } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; -import { exactCheck } from '../../../../../utils/build_validation/exact_check'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../../common/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts index ff62ea4443f3f6..9f9181359d44a7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts @@ -7,7 +7,7 @@ import { IsoDateString } from './iso_date_string'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('ios_date_string', () => { test('it should validate a iso string', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts index 2a97c8a4a143e8..dc0bd6cacf0d64 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts @@ -7,7 +7,7 @@ import { ListsDefaultArray } from './lists_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('lists_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts index d6f21681df88fe..a3338c878bd715 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts @@ -7,7 +7,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_zero'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts index 26441745a7f29e..48ea2025b9b12c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts @@ -7,7 +7,7 @@ import { PositiveInteger } from './positive_integer'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts index 76f722274ce23a..3aaff7e00ad51a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts @@ -7,7 +7,7 @@ import { ReferencesDefaultArray } from './references_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { getPaths, foldLeftRight } from '../../../../../../common/test_utils'; describe('references_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts index 76e3445358dd9b..41c0faf4d608d7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts @@ -7,7 +7,7 @@ import { RiskScore } from './risk_score'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('risk_score', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts index 7b68dbcef2d7ed..b640b449e6b8ac 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts @@ -7,7 +7,7 @@ import { UUID } from './uuid'; import { pipe } from 'fp-ts/lib/pipeable'; import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { foldLeftRight, getPaths } from '../../../../../../common/test_utils'; describe('uuid', () => { test('it should validate a uuid', () => { diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts index 1036a74b74a03c..adbfdbf6d6051c 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts @@ -5,9 +5,9 @@ */ import { set } from 'lodash/fp'; +import { RequestHandlerContext } from 'src/core/server'; import { SetupPlugins } from '../../../../plugin'; import { KibanaRequest } from '../../../../../../../../src/core/server'; -import { RequestHandlerContext } from '../../../../../../../../target/types/core/server'; import { FrameworkRequest } from '../../../framework'; export const buildFrameworkRequest = async ( diff --git a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts index 30b95dcfa94ee3..19353080c15cbf 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts @@ -7,13 +7,13 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; +import { formatErrors } from '../../../common/format_errors'; +import { exactCheck } from '../../../common/exact_check'; import { RouteValidationFunction, RouteValidationResultFactory, RouteValidationError, } from '../../../../../../src/core/server'; -import { exactCheck } from './exact_check'; -import { formatErrors } from './format_errors'; type RequestValidationResult = | { diff --git a/x-pack/plugins/snapshot_restore/public/application/constants/index.ts b/x-pack/plugins/snapshot_restore/public/application/constants/index.ts index ea6f5c80b9343d..ad95c19870d152 100644 --- a/x-pack/plugins/snapshot_restore/public/application/constants/index.ts +++ b/x-pack/plugins/snapshot_restore/public/application/constants/index.ts @@ -6,7 +6,7 @@ import { DAY } from '../../shared_imports'; -export const BASE_PATH = '/management/elasticsearch/snapshot_restore'; +export const BASE_PATH = '/management/data/snapshot_restore'; export const DEFAULT_SECTION: Section = 'snapshots'; export type Section = 'repositories' | 'snapshots' | 'restore_status' | 'policies'; diff --git a/x-pack/plugins/snapshot_restore/public/plugin.ts b/x-pack/plugins/snapshot_restore/public/plugin.ts index d966d0c32651c3..13a15c5711ba9d 100644 --- a/x-pack/plugins/snapshot_restore/public/plugin.ts +++ b/x-pack/plugins/snapshot_restore/public/plugin.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, PluginInitializerContext } from 'src/core/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { PLUGIN } from '../common/constants'; import { ClientConfigType } from './types'; @@ -40,12 +40,12 @@ export class SnapshotRestoreUIPlugin { textService.setup(i18n); httpService.setup(http); - management.sections.getSection('elasticsearch')!.registerApp({ + management.sections.getSection(ManagementSectionId.Data).registerApp({ id: PLUGIN.id, title: i18n.translate('xpack.snapshotRestore.appTitle', { defaultMessage: 'Snapshot and Restore', }), - order: 7, + order: 3, mount: async params => { const { mountManagementSection } = await import('./application/mount_management_section'); const services = { diff --git a/x-pack/plugins/snapshot_restore/server/plugin.ts b/x-pack/plugins/snapshot_restore/server/plugin.ts index 00ff3db976d661..c5d3c665a3b7f5 100644 --- a/x-pack/plugins/snapshot_restore/server/plugin.ts +++ b/x-pack/plugins/snapshot_restore/server/plugin.ts @@ -13,6 +13,7 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, Logger, PluginInitializerContext, @@ -31,10 +32,17 @@ export interface SnapshotRestoreContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + const esClientConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('snapshotRestore', esClientConfig); +} + export class SnapshotRestoreServerPlugin implements Plugin { private readonly logger: Logger; private readonly apiRoutes: ApiRoutes; private readonly license: License; + private snapshotRestoreESClient?: ICustomClusterClient; constructor(private context: PluginInitializerContext) { const { logger } = this.context; @@ -44,7 +52,7 @@ export class SnapshotRestoreServerPlugin implements Plugin } public async setup( - { http, elasticsearch }: CoreSetup, + { http, getStartServices }: CoreSetup, { licensing, security, cloud }: Dependencies ): Promise { const pluginConfig = await this.context.config @@ -72,11 +80,11 @@ export class SnapshotRestoreServerPlugin implements Plugin } ); - const esClientConfig = { plugins: [elasticsearchJsPlugin] }; - const snapshotRestoreESClient = elasticsearch.createClient('snapshotRestore', esClientConfig); - http.registerRouteHandlerContext('snapshotRestore', (ctx, request) => { + http.registerRouteHandlerContext('snapshotRestore', async (ctx, request) => { + this.snapshotRestoreESClient = + this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices)); return { - client: snapshotRestoreESClient.asScoped(request), + client: this.snapshotRestoreESClient.asScoped(request), }; }); @@ -95,11 +103,11 @@ export class SnapshotRestoreServerPlugin implements Plugin }); } - public start() { - this.logger.debug('Starting plugin'); - } + public start() {} public stop() { - this.logger.debug('Stopping plugin'); + if (this.snapshotRestoreESClient) { + this.snapshotRestoreESClient.close(); + } } } diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts index 782c261be9664c..7f8acccf71d887 100644 --- a/x-pack/plugins/spaces/public/management/management_service.test.ts +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -39,7 +39,7 @@ describe('ManagementService', () => { expect(mockKibanaSection.registerApp).toHaveBeenCalledWith({ id: 'spaces', title: 'Spaces', - order: 10, + order: 2, mount: expect.any(Function), }); }); diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index cec4bee1373ca8..297df076368495 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ManagementSetup, ManagementApp } from 'src/plugins/management/public'; import { StartServicesAccessor, Capabilities } from 'src/core/public'; +import { + ManagementSetup, + ManagementApp, + ManagementSectionId, +} from '../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../../security/public'; import { SpacesManager } from '../spaces_manager'; import { PluginsStart } from '../plugin'; @@ -25,12 +29,11 @@ export class ManagementService { private registeredSpacesManagementApp?: ManagementApp; public setup({ getStartServices, management, spacesManager, securityLicense }: SetupDeps) { - const kibanaSection = management.sections.getSection('kibana'); - if (kibanaSection) { - this.registeredSpacesManagementApp = kibanaSection.registerApp( + this.registeredSpacesManagementApp = management.sections + .getSection(ManagementSectionId.Kibana) + .registerApp( spacesManagementApp.create({ getStartServices, spacesManager, securityLicense }) ); - } } public start({ capabilities }: StartDeps) { diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index 61c872ec9269cc..92c78d63d1b2ea 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -70,7 +70,7 @@ describe('spacesManagementApp', () => { Object { "id": "spaces", "mount": [Function], - "order": 10, + "order": 2, "title": "Spaces", } `); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index 92b369807b0dab..079cf2234b13bf 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -28,7 +28,7 @@ export const spacesManagementApp = Object.freeze({ create({ getStartServices, spacesManager, securityLicense }: CreateParams) { return { id: this.id, - order: 10, + order: 2, title: i18n.translate('xpack.spaces.displayName', { defaultMessage: 'Spaces', }), diff --git a/x-pack/plugins/spaces/public/plugin.test.ts b/x-pack/plugins/spaces/public/plugin.test.ts index d879a318fe815c..a98f593f546a0e 100644 --- a/x-pack/plugins/spaces/public/plugin.test.ts +++ b/x-pack/plugins/spaces/public/plugin.test.ts @@ -7,7 +7,7 @@ import { coreMock } from 'src/core/public/mocks'; import { SpacesPlugin } from './plugin'; import { homePluginMock } from '../../../../src/plugins/home/public/mocks'; -import { ManagementSection } from '../../../../src/plugins/management/public'; +import { ManagementSection, ManagementSectionId } from '../../../../src/plugins/management/public'; import { managementPluginMock } from '../../../../src/plugins/management/public/mocks'; import { advancedSettingsMock } from '../../../../src/plugins/advanced_settings/public/mocks'; import { featuresPluginMock } from '../../features/public/mocks'; @@ -35,7 +35,7 @@ describe('Spaces plugin', () => { const kibanaSection = new ManagementSection( { - id: 'kibana', + id: ManagementSectionId.Kibana, title: 'Mock Kibana Section', order: 1, }, diff --git a/x-pack/plugins/transform/public/app/constants/index.ts b/x-pack/plugins/transform/public/app/constants/index.ts index 5d71980c83714a..3156fae7545b1e 100644 --- a/x-pack/plugins/transform/public/app/constants/index.ts +++ b/x-pack/plugins/transform/public/app/constants/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CLIENT_BASE_PATH = '/management/elasticsearch/transform/'; +export const CLIENT_BASE_PATH = '/management/data/transform/'; export enum SECTION_SLUG { HOME = 'transform_management', diff --git a/x-pack/plugins/transform/public/plugin.ts b/x-pack/plugins/transform/public/plugin.ts index 563a569fe95b53..1411f80cecd2ea 100644 --- a/x-pack/plugins/transform/public/plugin.ts +++ b/x-pack/plugins/transform/public/plugin.ts @@ -7,8 +7,8 @@ import { i18n as kbnI18n } from '@kbn/i18n'; import { CoreSetup } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ManagementSetup } from 'src/plugins/management/public'; import { HomePublicPluginSetup } from 'src/plugins/home/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { registerFeature } from './register_feature'; export interface PluginsDependencies { @@ -22,20 +22,18 @@ export class TransformUiPlugin { const { management, home } = pluginsSetup; // Register management section - const esSection = management.sections.getSection('elasticsearch'); - if (esSection !== undefined) { - esSection.registerApp({ - id: 'transform', - title: kbnI18n.translate('xpack.transform.appTitle', { - defaultMessage: 'Transforms', - }), - order: 4, - mount: async params => { - const { mountManagementSection } = await import('./app/mount_management_section'); - return mountManagementSection(coreSetup, params); - }, - }); - } + const esSection = management.sections.getSection(ManagementSectionId.Data); + esSection.registerApp({ + id: 'transform', + title: kbnI18n.translate('xpack.transform.appTitle', { + defaultMessage: 'Transforms', + }), + order: 5, + mount: async params => { + const { mountManagementSection } = await import('./app/mount_management_section'); + return mountManagementSection(coreSetup, params); + }, + }); registerFeature(home); } diff --git a/x-pack/plugins/transform/public/register_feature.ts b/x-pack/plugins/transform/public/register_feature.ts index 708dfcb70c67af..c81a18a3def872 100644 --- a/x-pack/plugins/transform/public/register_feature.ts +++ b/x-pack/plugins/transform/public/register_feature.ts @@ -22,7 +22,7 @@ export const registerFeature = (home: HomePublicPluginSetup) => { 'Use transforms to pivot existing Elasticsearch indices into summarized or entity-centric indices.', }), icon: 'managementApp', // there is currently no Transforms icon, so using the general management app icon - path: '/app/kibana#/management/elasticsearch/transform', + path: '/app/kibana#/management/data/transform', showOnHomePage: true, category: FeatureCatalogueCategory.ADMIN, }); diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index 3737377de2d5ee..56be8d7bb7de7a 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -9,12 +9,9 @@ export { XJsonMode, collapseLiteralStrings, expandLiteralStrings, -} from '../../../../src/plugins/es_ui_shared/public'; - -export { UseRequestConfig, useRequest, -} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +} from '../../../../src/plugins/es_ui_shared/public'; export { getErrorMessage, diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index 7da991bc02b376..c8057a3e2fae11 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, + ICustomClusterClient, Plugin, IScopedClusterClient, Logger, @@ -38,10 +39,18 @@ const PLUGIN = { }), }; +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + return core.elasticsearch.legacy.createClient('transform', { + plugins: [elasticsearchJsPlugin], + }); +} + export class TransformServerPlugin implements Plugin<{}, void, any, any> { private readonly apiRoutes: ApiRoutes; private readonly license: License; private readonly logger: Logger; + private transformESClient?: ICustomClusterClient; constructor(initContext: PluginInitializerContext) { this.logger = initContext.logger.get(); @@ -49,7 +58,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { this.license = new License(); } - setup({ elasticsearch, http }: CoreSetup, { licensing }: Dependencies): {} { + setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies): {} { const router = http.createRouter(); this.license.setup( @@ -72,12 +81,11 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { }); // Can access via new platform router's handler function 'context' parameter - context.transform.client - const transformClient = elasticsearch.createClient('transform', { - plugins: [elasticsearchJsPlugin], - }); - http.registerRouteHandlerContext('transform', (context, request) => { + http.registerRouteHandlerContext('transform', async (context, request) => { + this.transformESClient = + this.transformESClient ?? (await getCustomEsClient(getStartServices)); return { - dataClient: transformClient.asScoped(request), + dataClient: this.transformESClient.asScoped(request), }; }); @@ -85,5 +93,10 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { } start() {} - stop() {} + + stop() { + if (this.transformESClient) { + this.transformESClient.close(); + } + } } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a50a95bfdab383..219e922a0158c9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -124,6 +124,8 @@ "advancedSettings.searchBar.unableToParseQueryErrorMessage": "クエリをパースできません", "advancedSettings.searchBarAriaLabel": "高度な設定を検索", "advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "{query} を検索しました。{sectionLenght, plural, one {# セクション} other {# セクション}}に{optionLenght, plural, one {# オプション} other {# オプション}}があります。", + "charts.advancedSettings.visualization.colorMappingText": "ビジュアライゼーション内の特定の色のマップ値です", + "charts.advancedSettings.visualization.colorMappingTitle": "カラーマッピング", "charts.colormaps.bluesText": "青", "charts.colormaps.greensText": "緑", "charts.colormaps.greenToRedText": "緑から赤", @@ -538,6 +540,10 @@ "dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage": "「6.1.0」のダッシュボードの互換性のため、パネルデータを移行できませんでした。パネルには想定された列または行フィールドがありません", "dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage": "「6.3.0」のダッシュボードの互換性のため、パネルデータを移行できませんでした。パネルに必要なフィールドがありません: {key}", "data.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} と {lt} {to}", + "data.advancedSettings.docTableHighlightText": "ディスカバリと保存された検索ダッシュボードの結果をハイライトします。ハイライトすることで、大きなドキュメントを扱う際にリクエストが遅くなります。", + "data.advancedSettings.docTableHighlightTitle": "結果をハイライト", + "data.advancedSettings.metaFieldsText": "_source の外にあり、ドキュメントが表示される時に融合されるフィールドです", + "data.advancedSettings.metaFieldsTitle": "メタフィールド", "data.common.kql.errors.endOfInputText": "インプットの終わり", "data.common.kql.errors.fieldNameText": "フィールド名", "data.common.kql.errors.literalText": "文字通り", @@ -847,6 +853,28 @@ "devTools.badge.readOnly.text": "読み込み専用", "devTools.badge.readOnly.tooltip": "を保存できませんでした", "devTools.k7BreadcrumbsDevToolsLabel": "開発ツール", + "discover.advancedSettings.aggsTermsSizeText": "「可視化」ボタンをクリックした際に、フィールドドロップダウンやディスカバリサイドバーに可視化される用語の数を設定します。", + "discover.advancedSettings.aggsTermsSizeTitle": "用語数", + "discover.advancedSettings.context.defaultSizeText": "コンテキストビューに表示される周りのエントリーの数", + "discover.advancedSettings.context.defaultSizeTitle": "コンテキストサイズ", + "discover.advancedSettings.context.sizeStepText": "コンテキストサイズを増減させる際の最低単位です", + "discover.advancedSettings.context.sizeStepTitle": "コンテキストサイズのステップ", + "discover.advancedSettings.context.tieBreakerFieldsText": "同じタイムスタンプ値のドキュメントを区別するためのコンマ区切りのフィールドのリストです。このリストから、現在のインデックスパターンに含まれ並べ替え可能な初めのフィールドが使用されます。", + "discover.advancedSettings.context.tieBreakerFieldsTitle": "タイブレーカーフィールド", + "discover.advancedSettings.defaultColumnsText": "デフォルトでディスカバリタブに表示される列です", + "discover.advancedSettings.defaultColumnsTitle": "デフォルトの列", + "discover.advancedSettings.docTableHideTimeColumnText": "ディスカバリと、ダッシュボードのすべての保存された検索で、「時刻」列を非表示にします。", + "discover.advancedSettings.docTableHideTimeColumnTitle": "「時刻」列を非表示", + "discover.advancedSettings.fieldsPopularLimitText": "最も頻繁に使用されるフィールドのトップ N を表示します", + "discover.advancedSettings.fieldsPopularLimitTitle": "頻繁に使用されるフィールドの制限", + "discover.advancedSettings.sampleSizeText": "表に表示する行数です", + "discover.advancedSettings.sampleSizeTitle": "行数", + "discover.advancedSettings.searchOnPageLoadText": "ディスカバリの最初の読み込み時に検索を実行するかを制御します。この設定は、保存された検索の読み込み時には影響しません。", + "discover.advancedSettings.searchOnPageLoadTitle": "ページの読み込み時の検索", + "discover.advancedSettings.sortDefaultOrderText": "ディスカバリアプリのインデックスパターンに基づく時刻のデフォルトの並べ替え方向をコントロールします。", + "discover.advancedSettings.sortDefaultOrderTitle": "デフォルトの並べ替え方向", + "discover.advancedSettings.sortOrderAsc": "昇順", + "discover.advancedSettings.sortOrderDesc": "降順", "discover.docViews.json.codeEditorAriaLabel": "Elasticsearch ドキュメントの JSON ビューのみを読み込む", "discover.docViews.json.jsonTitle": "JSON", "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告", @@ -1945,12 +1973,6 @@ "inspector.requests.statisticsTabLabel": "統計", "inspector.title": "インスペクター", "inspector.view": "{viewName} を表示", - "kbn.advancedSettings.context.defaultSizeText": "コンテキストビューに表示される周りのエントリーの数", - "kbn.advancedSettings.context.defaultSizeTitle": "コンテキストサイズ", - "kbn.advancedSettings.context.sizeStepText": "コンテキストサイズを増減させる際の最低単位です", - "kbn.advancedSettings.context.sizeStepTitle": "コンテキストサイズのステップ", - "kbn.advancedSettings.context.tieBreakerFieldsText": "同じタイムスタンプ値のドキュメントを区別するためのコンマ区切りのフィールドのリストです。このリストから、現在のインデックスパターンに含まれ並べ替え可能な初めのフィールドが使用されます。", - "kbn.advancedSettings.context.tieBreakerFieldsTitle": "タイブレーカーフィールド", "kbn.advancedSettings.courier.batchSearchesText": "無効の場合、ダッシュボードパネルは個々に読み込まれ、検索リクエストはユーザーが移動するか\n クエリを更新すると停止します。有効の場合、ダッシュボードパネルはすべてのデータが読み込まれると同時に読み込まれ、\n 検索は停止しません。", "kbn.advancedSettings.courier.batchSearchesTextDeprecation": "この設定はサポートが終了し、Kibana 8.0 では削除されます。", "kbn.advancedSettings.courier.batchSearchesTitle": "同時検索のバッチ処理", @@ -1985,8 +2007,6 @@ "kbn.advancedSettings.dateNanosFormatText": "Elasticsearch の {dateNanosLink} データタイプに使用されます", "kbn.advancedSettings.dateNanosFormatTitle": "ナノ秒フォーマットでの日付", "kbn.advancedSettings.dateNanosLinkTitle": "date_nanos", - "kbn.advancedSettings.defaultColumnsText": "デフォルトでディスカバリタブに表示される列です", - "kbn.advancedSettings.defaultColumnsTitle": "デフォルトの列", "kbn.advancedSettings.defaultIndexText": "インデックスが設定されていない時にアクセスするインデックスです", "kbn.advancedSettings.defaultIndexTitle": "デフォルトのインデックス", "kbn.advancedSettings.defaultRoute.defaultRouteIsRelativeValidationMessage": "相対 URL でなければなりません。", @@ -1994,22 +2014,6 @@ "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "デフォルトのルート", "kbn.advancedSettings.disableAnimationsText": "Kibana UI の不要なアニメーションをオフにします。変更を適用するにはページを更新してください。", "kbn.advancedSettings.disableAnimationsTitle": "アニメーションを無効にする", - "kbn.advancedSettings.discover.aggsTermsSizeText": "「可視化」ボタンをクリックした際に、フィールドドロップダウンやディスカバリサイドバーに可視化される用語の数を設定します。", - "kbn.advancedSettings.discover.aggsTermsSizeTitle": "用語数", - "kbn.advancedSettings.discover.sampleSizeText": "表に表示する行数です", - "kbn.advancedSettings.discover.sampleSizeTitle": "行数", - "kbn.advancedSettings.discover.searchOnPageLoadText": "ディスカバリの最初の読み込み時に検索を実行するかを制御します。この設定は、保存された検索の読み込み時には影響しません。", - "kbn.advancedSettings.discover.searchOnPageLoadTitle": "ページの読み込み時の検索", - "kbn.advancedSettings.discover.sortDefaultOrderText": "ディスカバリアプリのインデックスパターンに基づく時刻のデフォルトの並べ替え方向をコントロールします。", - "kbn.advancedSettings.discover.sortDefaultOrderTitle": "デフォルトの並べ替え方向", - "kbn.advancedSettings.discover.sortOrderAsc": "昇順", - "kbn.advancedSettings.discover.sortOrderDesc": "降順", - "kbn.advancedSettings.docTableHideTimeColumnText": "ディスカバリと、ダッシュボードのすべての保存された検索で、「時刻」列を非表示にします。", - "kbn.advancedSettings.docTableHideTimeColumnTitle": "「時刻」列を非表示", - "kbn.advancedSettings.docTableHighlightText": "ディスカバリと保存された検索ダッシュボードの結果をハイライトします。ハイライトすることで、大きなドキュメントを扱う際にリクエストが遅くなります。", - "kbn.advancedSettings.docTableHighlightTitle": "結果をハイライト", - "kbn.advancedSettings.fieldsPopularLimitText": "最も頻繁に使用されるフィールドのトップ N を表示します", - "kbn.advancedSettings.fieldsPopularLimitTitle": "頻繁に使用されるフィールドの制限", "kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数字フォーマット", "kbn.advancedSettings.format.bytesFormatText": "「バイト」フォーマットのデフォルト {numeralFormatLink} です", "kbn.advancedSettings.format.bytesFormatTitle": "バイトフォーマット", @@ -2035,12 +2039,8 @@ "kbn.advancedSettings.historyLimitTitle": "履歴制限数", "kbn.advancedSettings.indexPatternPlaceholderText": "「管理 > インデックスパターン > インデックスパターンを作成」で使用される「インデックスパターン名」フィールドのプレースホルダーです。", "kbn.advancedSettings.indexPatternPlaceholderTitle": "インデックスパターンのプレースホルダー", - "kbn.advancedSettings.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です", - "kbn.advancedSettings.maxBucketsTitle": "バケットの最大数", "kbn.advancedSettings.maxCellHeightText": "表のセルが使用する高さの上限です。この切り捨てを無効にするには 0 に設定します", "kbn.advancedSettings.maxCellHeightTitle": "表のセルの高さの上限", - "kbn.advancedSettings.metaFieldsText": "_source の外にあり、ドキュメントが表示される時に融合されるフィールドです", - "kbn.advancedSettings.metaFieldsTitle": "メタフィールド", "kbn.advancedSettings.notifications.banner.markdownLinkText": "マークダウン対応", "kbn.advancedSettings.notifications.bannerLifetimeText": "バナー通知が画面に表示されるミリ秒単位での時間です。{infinityValue} に設定するとカウントダウンが無効になります。", "kbn.advancedSettings.notifications.bannerLifetimeTitle": "バナー通知時間", @@ -2059,10 +2059,6 @@ "kbn.advancedSettings.query.queryStringOptions.optionsLinkText": "オプション", "kbn.advancedSettings.query.queryStringOptionsText": "Lucene クエリ文字列パーサーの {optionsLink}「{queryLanguage}」が {luceneLanguage} に設定されている時にのみ使用されます。", "kbn.advancedSettings.query.queryStringOptionsTitle": "クエリ文字列のオプション", - "kbn.advancedSettings.savedObjects.listingLimitText": "一覧ページ用に取得するオブジェクトの数です", - "kbn.advancedSettings.savedObjects.listingLimitTitle": "オブジェクト取得制限", - "kbn.advancedSettings.savedObjects.perPageText": "読み込みダイアログで表示されるページごとのオブジェクトの数です", - "kbn.advancedSettings.savedObjects.perPageTitle": "ページごとのオブジェクト数", "kbn.advancedSettings.searchQueryLanguageKql": "KQL", "kbn.advancedSettings.searchQueryLanguageLucene": "Lucene", "kbn.advancedSettings.searchQueryLanguageText": "クエリバーで使用されるクエリ言語です。KQL は Kibana 用に特別に開発された新しい言語です。", @@ -2093,14 +2089,6 @@ "kbn.advancedSettings.timepicker.timeDefaultsText": "時間フィルターが選択されずに Kibana が起動した際に使用される時間フィルターです", "kbn.advancedSettings.timepicker.timeDefaultsTitle": "デフォルトのタイムピッカー", "kbn.advancedSettings.timepicker.today": "今日", - "kbn.advancedSettings.visualization.colorMappingText": "ビジュアライゼーション内の特定の色のマップ値です", - "kbn.advancedSettings.visualization.colorMappingTitle": "カラーマッピング", - "kbn.advancedSettings.visualization.dimmingOpacityText": "チャートの別のエレメントが選択された時に暗くなるチャート項目の透明度です。この数字が小さければ小さいほど、ハイライトされたエレメントが目立ちます。0 と 1 の間の数字で設定します。", - "kbn.advancedSettings.visualization.dimmingOpacityTitle": "減光透明度", - "kbn.advancedSettings.visualization.heatmap.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です。値が大きいとブラウザのレンダリング速度が下がる可能性があります。", - "kbn.advancedSettings.visualization.heatmap.maxBucketsTitle": "ヒートマップの最大バケット数", - "kbn.advancedSettings.visualization.loadingDelayText": "クエリの際にビジュアライゼーションを暗くするまでの時間です", - "kbn.advancedSettings.visualization.loadingDelayTitle": "読み込み遅延", "kbn.advancedSettings.visualization.showRegionMapWarningsText": "用語がマップの形に合わない場合に地域マップに警告を表示するかどうかです。", "kbn.advancedSettings.visualization.showRegionMapWarningsTitle": "地域マップに警告を表示", "kbn.advancedSettings.visualization.tileMap.maxPrecision.cellDimensionsLinkText": "ディメンションの説明", @@ -2431,7 +2419,6 @@ "kibana-react.tableListView.listing.table.editActionDescription": "編集", "kibana-react.tableListView.listing.table.editActionName": "編集", "kibana-react.tableListView.listing.unableToDeleteDangerMessage": "{entityName} を削除できません", - "management.connectDataDisplayName": "データに接続", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン", "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン", @@ -2465,6 +2452,10 @@ "regionMap.visParams.vectorMapLabel": "ベクトルマップ", "regionMap.visualization.unableToShowMismatchesWarningText": "次の各用語がシェイプの結合フィールドのシェイプと一致することを確認してください: {mismatches}", "regionMap.visualization.unableToShowMismatchesWarningTitle": "{mismatchesLength} {oneMismatch, plural, one { 件の結果} other { 件の結果}}をマップに表示できません", + "savedObjects.advancedSettings.listingLimitText": "一覧ページ用に取得するオブジェクトの数です", + "savedObjects.advancedSettings.listingLimitTitle": "オブジェクト取得制限", + "savedObjects.advancedSettings.perPageText": "読み込みダイアログで表示されるページごとのオブジェクトの数です", + "savedObjects.advancedSettings.perPageTitle": "ページごとのオブジェクト数", "savedObjects.confirmModal.cancelButtonLabel": "キャンセル", "savedObjects.confirmModal.overwriteButtonLabel": "上書き", "savedObjects.confirmModal.overwriteConfirmationMessage": "{title} を上書きしてよろしいですか?", @@ -3156,6 +3147,8 @@ "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "削除", "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "再度有効にする", "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "一時的に無効にする", + "visTypeTimeseries.advancedSettings.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です", + "visTypeTimeseries.advancedSettings.maxBucketsTitle": "バケットの最大数", "visTypeTimeseries.aggLookup.averageLabel": "平均", "visTypeTimeseries.aggLookup.calculationLabel": "計算", "visTypeTimeseries.aggLookup.cardinalityLabel": "基数", @@ -3714,6 +3707,10 @@ "visTypeVega.visualization.renderErrorTitle": "Vega エラー", "visTypeVega.visualization.unableToFindDefaultIndexErrorMessage": "デフォルトのインデックスが見つかりません", "visTypeVega.visualization.unableToRenderWithoutDataWarningMessage": "データなしにはレンダリングできません", + "visTypeVislib.advancedSettings.visualization.dimmingOpacityText": "チャートの別のエレメントが選択された時に暗くなるチャート項目の透明度です。この数字が小さければ小さいほど、ハイライトされたエレメントが目立ちます。0 と 1 の間の数字で設定します。", + "visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle": "減光透明度", + "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です。値が大きいとブラウザのレンダリング速度が下がる可能性があります。", + "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "ヒートマップの最大バケット数", "visTypeVislib.aggResponse.allDocsTitle": "すべてのドキュメント", "visTypeVislib.area.areaDescription": "折れ線グラフの下の数量を強調します。", "visTypeVislib.area.areaTitle": "エリア", @@ -4761,8 +4758,6 @@ "xpack.beatsManagement.breadcrumb.beatTags": "{beatId} のビートタグ", "xpack.beatsManagement.breadcrumb.configurationTags": "構成タグ", "xpack.beatsManagement.breadcrumb.enrolledBeats": "登録済みのビート", - "xpack.beatsManagement.centralManagementLinkLabel": "集中管理", - "xpack.beatsManagement.centralManagementSectionLabel": "ビート", "xpack.beatsManagement.config.other.error": "有効な YAML フォーマットを使用してください", "xpack.beatsManagement.config.otherConfigDescription": "YAML フォーマットで Filebeat インプットの他の設定を指定します", "xpack.beatsManagement.config.otherConfigLabel": "他の構成", @@ -6736,7 +6731,6 @@ "xpack.idxMgmt.indexTable.serverErrorTitle": "インデックスの読み込み中にエラーが発生", "xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel": "インデックスの検索", "xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder": "検索", - "xpack.idxMgmt.indexTable.systemIndicesSwitchLabel": "システムインデックスを含める", "xpack.idxMgmt.indexTemplatesList.emptyPrompt.noIndexTemplatesTitle": "まだテンプレートがありません", "xpack.idxMgmt.indexTemplatesList.loadingIndexTemplatesDescription": "テンプレートを読み込み中…", "xpack.idxMgmt.indexTemplatesList.loadingIndexTemplatesErrorMessage": "テンプレートの読み込み中にエラーが発生", @@ -9978,7 +9972,6 @@ "xpack.ml.management.jobsList.noGrantedPrivilegesDescription": "ML ジョブを管理するパーミッションがありません", "xpack.ml.management.jobsList.noPermissionToAccessLabel": "ML ジョブへのアクセスにはパーミッションが必要です", "xpack.ml.management.jobsListTitle": "ジョブリスト", - "xpack.ml.management.mlTitle": "機械学習", "xpack.ml.messagebarService.errorTitle": "エラーが発生しました", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 他のすべてのリクエストはキャンセルされました。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "フィールド値の例のサンプルをトークン化することができませんでした。{message}", @@ -12830,7 +12823,6 @@ "xpack.security.management.roles.statusColumnName": "ステータス", "xpack.security.management.roles.subtitle": "ユーザーのグループにロールを適用してスタック全体のパーミッションを管理", "xpack.security.management.rolesTitle": "ロール", - "xpack.security.management.securityTitle": "セキュリティ", "xpack.security.management.users.confirmDelete.cancelButtonLabel": "キャンセル", "xpack.security.management.users.confirmDelete.confirmButtonLabel": "削除", "xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle": "{userLength} ユーザーの削除", @@ -13132,7 +13124,6 @@ "xpack.siem.case.caseView.pushToServiceDisableByConfigTitle": "Kibana の構成ファイルで ServiceNow を有効にする", "xpack.siem.case.caseView.pushToServiceDisableByLicenseDescription": "外部システムでケースを開くには、ライセンスをプラチナに更新するか、30 日間の無料トライアルを開始するか、AWS、GCP、または Azure で {link} にサインアップする必要があります。", "xpack.siem.case.caseView.pushToServiceDisableByLicenseTitle": "E lastic Platinum へのアップグレード", - "xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigDescription": "外部システムでケースを開いて更新するには、{link} を設定する必要があります。", "xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigTitle": "外部コネクターを構成", "xpack.siem.case.caseView.reopenCase": "ケースを再開", "xpack.siem.case.caseView.reopenedCase": "ケースを再開する", @@ -13308,8 +13299,8 @@ "xpack.siem.detectionEngine.components.importRuleModal.overwriteDescription": "保存されたオブジェクトを同じルールIDで自動的に上書きします", "xpack.siem.detectionEngine.components.importRuleModal.selectRuleDescription": "インポートする SIEM ルール (検出エンジンビューからエクスポートしたもの) を選択します", "xpack.siem.detectionEngine.components.importRuleModal.successfullyImportedRulesTitle": "{totalRules} {totalRules, plural, =1 {ルール} other {ルール}}を正常にインポートしました", - "xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithActivatingTitle": "ルールの作成と有効化", - "xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithoutActivatingTitle": "有効化せずにルールを作成", + "xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithActivatingTitle": "ルールの作成と有効化", + "xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithoutActivatingTitle": "有効化せずにルールを作成", "xpack.siem.detectionEngine.createRule.backToRulesDescription": "シグナル検出ルールに戻る", "xpack.siem.detectionEngine.createRule.editRuleButton": "編集", "xpack.siem.detectionEngine.createRule.filtersLabel": "フィルター", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b984c7ad94eb04..954162647bf830 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -124,6 +124,8 @@ "advancedSettings.searchBar.unableToParseQueryErrorMessage": "无法解析查询", "advancedSettings.searchBarAriaLabel": "搜索高级设置", "advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "您搜索了“{query}”。{sectionLenght, plural, one {# 个部分} other {# 个部分}}中有 {optionLenght, plural, one {# 个选项} other {# 个选项}}", + "charts.advancedSettings.visualization.colorMappingText": "将值映射到可视化内的指定颜色", + "charts.advancedSettings.visualization.colorMappingTitle": "颜色映射", "charts.colormaps.bluesText": "蓝色", "charts.colormaps.greensText": "绿色", "charts.colormaps.greenToRedText": "绿到红", @@ -537,6 +539,10 @@ "dashboard.panel.removePanel.replacePanel": "替换面板", "dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage": "无法迁移用于“6.1.0”向后兼容的面板数据,面板不包含所需的列和/或行字段", "dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage": "无法迁移用于“6.3.0”向后兼容的面板数据,面板不包含预期字段:{key}", + "data.advancedSettings.metaFieldsText": "_source 之外存在的、在显示我们的文档时将合并进其中的字段", + "data.advancedSettings.metaFieldsTitle": "元字段", + "data.advancedSettings.docTableHighlightText": "突出显示 Discover 和已保存搜索仪表板中的结果。处理大文档时,突出显示会使请求变慢。", + "data.advancedSettings.docTableHighlightTitle": "突出显示结果", "data.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} 和 {lt} {to}", "data.common.kql.errors.endOfInputText": "输入结束", "data.common.kql.errors.fieldNameText": "字段名称", @@ -848,6 +854,28 @@ "devTools.badge.readOnly.text": "只读", "devTools.badge.readOnly.tooltip": "无法保存", "devTools.k7BreadcrumbsDevToolsLabel": "开发工具", + "discover.advancedSettings.aggsTermsSizeText": "确定在单击“可视化”按钮时将在发现侧边栏的字段下拉列表中可视化多少个词。", + "discover.advancedSettings.aggsTermsSizeTitle": "词数目", + "discover.advancedSettings.context.defaultSizeText": "要在上下文视图中显示的周围条目数目", + "discover.advancedSettings.context.defaultSizeTitle": "上下文大小", + "discover.advancedSettings.context.sizeStepText": "递增或递减上下文大小的步进大小", + "discover.advancedSettings.context.sizeStepTitle": "上下文大小步进", + "discover.advancedSettings.context.tieBreakerFieldsText": "要在具有相同时间戳值的文档之间用于平分决胜的字段逗号分隔列表。将使用此列表上存在且在当前索引模式下可排序的第一个字段。", + "discover.advancedSettings.context.tieBreakerFieldsTitle": "平分决胜字段", + "discover.advancedSettings.docTableHideTimeColumnText": "在 Discover 中和仪表板上的所有已保存搜索中隐藏“时间”列。", + "discover.advancedSettings.docTableHideTimeColumnTitle": "隐藏“时间”列", + "discover.advancedSettings.fieldsPopularLimitText": "要显示的排名前 N 最常见字段", + "discover.advancedSettings.fieldsPopularLimitTitle": "常见字段限制", + "discover.advancedSettings.defaultColumnsText": "“发现”选项卡中默认显示的列", + "discover.advancedSettings.defaultColumnsTitle": "默认列", + "discover.advancedSettings.sampleSizeText": "要在表中显示的行数目", + "discover.advancedSettings.sampleSizeTitle": "行数目", + "discover.advancedSettings.searchOnPageLoadText": "控制在 Discover 首次加载时是否执行搜索。加载已保存搜索时,此设置无效。", + "discover.advancedSettings.searchOnPageLoadTitle": "在页面加载时搜索", + "discover.advancedSettings.sortDefaultOrderText": "在 Discover 应用中控制基于时间的索引模式的默认排序方向。", + "discover.advancedSettings.sortDefaultOrderTitle": "默认排序方向", + "discover.advancedSettings.sortOrderAsc": "升序", + "discover.advancedSettings.sortOrderDesc": "降序", "discover.docViews.json.codeEditorAriaLabel": "Elasticsearch 文档的只读 JSON 视图", "discover.docViews.json.jsonTitle": "JSON", "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告", @@ -1946,12 +1974,6 @@ "inspector.requests.statisticsTabLabel": "统计信息", "inspector.title": "检查器", "inspector.view": "视图:{viewName}", - "kbn.advancedSettings.context.defaultSizeText": "要在上下文视图中显示的周围条目数目", - "kbn.advancedSettings.context.defaultSizeTitle": "上下文大小", - "kbn.advancedSettings.context.sizeStepText": "递增或递减上下文大小的步进大小", - "kbn.advancedSettings.context.sizeStepTitle": "上下文大小步进", - "kbn.advancedSettings.context.tieBreakerFieldsText": "要在具有相同时间戳值的文档之间用于平分决胜的字段逗号分隔列表。将使用此列表上存在且在当前索引模式下可排序的第一个字段。", - "kbn.advancedSettings.context.tieBreakerFieldsTitle": "平分决胜字段", "kbn.advancedSettings.courier.batchSearchesText": "禁用时,仪表板面板将分别加载,用户离开时或更新查询时,\n 搜索请求将终止。启用时,仪表板面板将一起加载并加载所有数据,\n 搜索将不会终止。", "kbn.advancedSettings.courier.batchSearchesTextDeprecation": "此设置已过时,将在 Kibana 8.0 中移除。", "kbn.advancedSettings.courier.batchSearchesTitle": "批处理并发搜索", @@ -1986,8 +2008,6 @@ "kbn.advancedSettings.dateNanosFormatText": "用于 Elasticsearch 的 {dateNanosLink} 数据类型", "kbn.advancedSettings.dateNanosFormatTitle": "纳秒格式的日期", "kbn.advancedSettings.dateNanosLinkTitle": "date_nanos", - "kbn.advancedSettings.defaultColumnsText": "“发现”选项卡中默认显示的列", - "kbn.advancedSettings.defaultColumnsTitle": "默认列", "kbn.advancedSettings.defaultIndexText": "未设置索引时要访问的索引", "kbn.advancedSettings.defaultIndexTitle": "默认索引", "kbn.advancedSettings.defaultRoute.defaultRouteIsRelativeValidationMessage": "必须是相对 URL。", @@ -1995,22 +2015,6 @@ "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "默认路由", "kbn.advancedSettings.disableAnimationsText": "在 Kibana UI 中关闭所有没有必要的动画。刷新页面以应用更改。", "kbn.advancedSettings.disableAnimationsTitle": "禁用动画", - "kbn.advancedSettings.discover.aggsTermsSizeText": "确定在单击“可视化”按钮时将在发现侧边栏的字段下拉列表中可视化多少个词。", - "kbn.advancedSettings.discover.aggsTermsSizeTitle": "词数目", - "kbn.advancedSettings.discover.sampleSizeText": "要在表中显示的行数目", - "kbn.advancedSettings.discover.sampleSizeTitle": "行数目", - "kbn.advancedSettings.discover.searchOnPageLoadText": "控制在 Discover 首次加载时是否执行搜索。加载已保存搜索时,此设置无效。", - "kbn.advancedSettings.discover.searchOnPageLoadTitle": "在页面加载时搜索", - "kbn.advancedSettings.discover.sortDefaultOrderText": "在 Discover 应用中控制基于时间的索引模式的默认排序方向。", - "kbn.advancedSettings.discover.sortDefaultOrderTitle": "默认排序方向", - "kbn.advancedSettings.discover.sortOrderAsc": "升序", - "kbn.advancedSettings.discover.sortOrderDesc": "降序", - "kbn.advancedSettings.docTableHideTimeColumnText": "在 Discover 中和仪表板上的所有已保存搜索中隐藏“时间”列。", - "kbn.advancedSettings.docTableHideTimeColumnTitle": "隐藏“时间”列", - "kbn.advancedSettings.docTableHighlightText": "突出显示 Discover 和已保存搜索仪表板中的结果。处理大文档时,突出显示会使请求变慢。", - "kbn.advancedSettings.docTableHighlightTitle": "突出显示结果", - "kbn.advancedSettings.fieldsPopularLimitText": "要显示的排名前 N 最常见字段", - "kbn.advancedSettings.fieldsPopularLimitTitle": "常见字段限制", "kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数值格式", "kbn.advancedSettings.format.bytesFormatText": "“字节”格式的默认{numeralFormatLink}", "kbn.advancedSettings.format.bytesFormatTitle": "字节格式", @@ -2036,12 +2040,8 @@ "kbn.advancedSettings.historyLimitTitle": "历史记录限制", "kbn.advancedSettings.indexPatternPlaceholderText": "在“管理 > 索引模式 > 创建索引模式”中“索引模式名称”的占位符。", "kbn.advancedSettings.indexPatternPlaceholderTitle": "索引模式占位符", - "kbn.advancedSettings.maxBucketsText": "单个数据源可以返回的最大存储桶数目", - "kbn.advancedSettings.maxBucketsTitle": "最大存储桶数", "kbn.advancedSettings.maxCellHeightText": "表中单元格应占用的最大高度。设置为 0 可禁用截短", "kbn.advancedSettings.maxCellHeightTitle": "最大表单元格高度", - "kbn.advancedSettings.metaFieldsText": "_source 之外存在的、在显示我们的文档时将合并进其中的字段", - "kbn.advancedSettings.metaFieldsTitle": "元字段", "kbn.advancedSettings.notifications.banner.markdownLinkText": "Markdown 受支持", "kbn.advancedSettings.notifications.bannerLifetimeText": "在屏幕上显示横幅通知的时间(毫秒)。设置为 {infinityValue} 将禁用倒计时。", "kbn.advancedSettings.notifications.bannerLifetimeTitle": "横幅通知生存时间", @@ -2060,10 +2060,6 @@ "kbn.advancedSettings.query.queryStringOptions.optionsLinkText": "选项", "kbn.advancedSettings.query.queryStringOptionsText": "lucene 查询字符串解析器的{optionsLink}。只有将“{queryLanguage}”设置为 {luceneLanguage} 时才会使用。", "kbn.advancedSettings.query.queryStringOptionsTitle": "查询字符串选项", - "kbn.advancedSettings.savedObjects.listingLimitText": "为列表页面提取的对象数目", - "kbn.advancedSettings.savedObjects.listingLimitTitle": "对象列表限制", - "kbn.advancedSettings.savedObjects.perPageText": "在加载对话框中每页要显示的对象数目", - "kbn.advancedSettings.savedObjects.perPageTitle": "每页对象数", "kbn.advancedSettings.searchQueryLanguageKql": "KQL", "kbn.advancedSettings.searchQueryLanguageLucene": "Lucene", "kbn.advancedSettings.searchQueryLanguageText": "查询栏使用的查询语言。KQL 是专门为 Kibana 打造的新型语言。", @@ -2094,14 +2090,6 @@ "kbn.advancedSettings.timepicker.timeDefaultsText": "未使用时间筛选启动 Kibana 时要使用的时间筛选选择", "kbn.advancedSettings.timepicker.timeDefaultsTitle": "时间筛选默认值", "kbn.advancedSettings.timepicker.today": "今日", - "kbn.advancedSettings.visualization.colorMappingText": "将值映射到可视化内的指定颜色", - "kbn.advancedSettings.visualization.colorMappingTitle": "颜色映射", - "kbn.advancedSettings.visualization.dimmingOpacityText": "突出显示图表的其他元素时变暗图表项的透明度。此数字越低,突出显示的元素越突出。必须是介于 0 和 1 之间的数字。", - "kbn.advancedSettings.visualization.dimmingOpacityTitle": "变暗透明度", - "kbn.advancedSettings.visualization.heatmap.maxBucketsText": "单个数据源可以返回的最大存储桶数目。较高的数目可能对浏览器呈现性能有负面影响", - "kbn.advancedSettings.visualization.heatmap.maxBucketsTitle": "热图最大存储桶数", - "kbn.advancedSettings.visualization.loadingDelayText": "在查询期间在将可视化变暗之前要等待的时间", - "kbn.advancedSettings.visualization.loadingDelayTitle": "加载延迟", "kbn.advancedSettings.visualization.showRegionMapWarningsText": "词无法联接到地图上的形状时,区域地图是否显示警告。", "kbn.advancedSettings.visualization.showRegionMapWarningsTitle": "显示区域地图警告", "kbn.advancedSettings.visualization.tileMap.maxPrecision.cellDimensionsLinkText": "单元格维度的解释", @@ -2432,7 +2420,6 @@ "kibana-react.tableListView.listing.table.editActionDescription": "编辑", "kibana-react.tableListView.listing.table.editActionName": "编辑", "kibana-react.tableListView.listing.unableToDeleteDangerMessage": "无法删除{entityName}", - "management.connectDataDisplayName": "连接数据", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合", "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "标准索引模式", "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "索引模式", @@ -2466,6 +2453,10 @@ "regionMap.visParams.vectorMapLabel": "矢量地图", "regionMap.visualization.unableToShowMismatchesWarningText": "确保每个字词与该形状的联接字段匹配:{mismatches}", "regionMap.visualization.unableToShowMismatchesWarningTitle": "无法在地图上显示 {mismatchesLength} {oneMismatch, plural, one { 个结果} other { 个结果}}", + "savedObjects.advancedSettings.listingLimitText": "为列表页面提取的对象数目", + "savedObjects.advancedSettings.listingLimitTitle": "对象列表限制", + "savedObjects.advancedSettings.perPageText": "在加载对话框中每页要显示的对象数目", + "savedObjects.advancedSettings.perPageTitle": "每页对象数", "savedObjects.confirmModal.cancelButtonLabel": "取消", "savedObjects.confirmModal.overwriteButtonLabel": "覆盖", "savedObjects.confirmModal.overwriteConfirmationMessage": "确定要覆盖“{title}”?", @@ -3157,6 +3148,8 @@ "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "删除", "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "重新启用", "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "暂时禁用", + "visTypeTimeseries.advancedSettings.maxBucketsText": "单个数据源可以返回的最大存储桶数目", + "visTypeTimeseries.advancedSettings.maxBucketsTitle": "最大存储桶数", "visTypeTimeseries.aggLookup.averageLabel": "平均值", "visTypeTimeseries.aggLookup.calculationLabel": "计算", "visTypeTimeseries.aggLookup.cardinalityLabel": "基数", @@ -3715,6 +3708,10 @@ "visTypeVega.visualization.renderErrorTitle": "Vega 错误", "visTypeVega.visualization.unableToFindDefaultIndexErrorMessage": "找不到默认索引", "visTypeVega.visualization.unableToRenderWithoutDataWarningMessage": "没有数据时无法渲染", + "visTypeVislib.advancedSettings.visualization.dimmingOpacityText": "突出显示图表的其他元素时变暗图表项的透明度。此数字越低,突出显示的元素越突出。必须是介于 0 和 1 之间的数字。", + "visTypeVislib.advancedSettings.visualization.dimmingOpacityTitle": "变暗透明度", + "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "单个数据源可以返回的最大存储桶数目。较高的数目可能对浏览器呈现性能有负面影响", + "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "热图最大存储桶数", "visTypeVislib.aggResponse.allDocsTitle": "所有文档", "visTypeVislib.area.areaDescription": "突出折线图下方的数量", "visTypeVislib.area.areaTitle": "面积图", @@ -4762,8 +4759,6 @@ "xpack.beatsManagement.breadcrumb.beatTags": "{beatId} 的 Beat 标记", "xpack.beatsManagement.breadcrumb.configurationTags": "配置标记", "xpack.beatsManagement.breadcrumb.enrolledBeats": "已注册 Beats", - "xpack.beatsManagement.centralManagementLinkLabel": "集中管理", - "xpack.beatsManagement.centralManagementSectionLabel": "Beats", "xpack.beatsManagement.config.other.error": "使用有效的 YAML 格式", "xpack.beatsManagement.config.otherConfigDescription": "使用 YAML 格式指定 Filebeat 输入的其他设置", "xpack.beatsManagement.config.otherConfigLabel": "其他配置", @@ -6741,7 +6736,6 @@ "xpack.idxMgmt.indexTable.serverErrorTitle": "加载索引时出错", "xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel": "搜索索引", "xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder": "搜索", - "xpack.idxMgmt.indexTable.systemIndicesSwitchLabel": "包括系统索引", "xpack.idxMgmt.indexTemplatesList.emptyPrompt.noIndexTemplatesTitle": "您尚未有任何模板", "xpack.idxMgmt.indexTemplatesList.loadingIndexTemplatesDescription": "正在加载模板……", "xpack.idxMgmt.indexTemplatesList.loadingIndexTemplatesErrorMessage": "加载模板时出错", @@ -9984,7 +9978,6 @@ "xpack.ml.management.jobsList.noGrantedPrivilegesDescription": "您无权管理 ML 作业", "xpack.ml.management.jobsList.noPermissionToAccessLabel": "您需要访问 ML 作业的权限", "xpack.ml.management.jobsListTitle": "作业列表", - "xpack.ml.management.mlTitle": "Machine Learning", "xpack.ml.messagebarService.errorTitle": "发生了错误", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 所有其他请求已取消。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "无法对示例字段值样本进行分词。{message}", @@ -12837,7 +12830,6 @@ "xpack.security.management.roles.statusColumnName": "状态", "xpack.security.management.roles.subtitle": "将角色应用到用户组并管理整个堆栈的权限。", "xpack.security.management.rolesTitle": "角色", - "xpack.security.management.securityTitle": "安全性", "xpack.security.management.users.confirmDelete.cancelButtonLabel": "取消", "xpack.security.management.users.confirmDelete.confirmButtonLabel": "删除", "xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle": "删除 {userLength} 用户", @@ -13139,7 +13131,6 @@ "xpack.siem.case.caseView.pushToServiceDisableByConfigTitle": "在 Kibana 配置文件中启用 ServiceNow", "xpack.siem.case.caseView.pushToServiceDisableByLicenseDescription": "要在外部系统中打开案例,必须将许可证更新到白金级,开始为期 30 天的免费试用,或在 AWS、GCP 或 Azure 上快速部署 {link}。", "xpack.siem.case.caseView.pushToServiceDisableByLicenseTitle": "升级到 Elastic 白金级", - "xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigDescription": "要在外部系统上打开和更新案例,必须配置 {link}。", "xpack.siem.case.caseView.pushToServiceDisableByNoCaseConfigTitle": "配置外部连接器", "xpack.siem.case.caseView.reopenCase": "重新打开案例", "xpack.siem.case.caseView.reopenedCase": "重新打开的案例", @@ -13315,8 +13306,8 @@ "xpack.siem.detectionEngine.components.importRuleModal.overwriteDescription": "自动覆盖具有相同规则 ID 的已保存对象", "xpack.siem.detectionEngine.components.importRuleModal.selectRuleDescription": "选择要导入的 SIEM 规则(如从检测引擎视图导出的)", "xpack.siem.detectionEngine.components.importRuleModal.successfullyImportedRulesTitle": "已成功导入 {totalRules} 个{totalRules, plural, =1 {规则} other {规则}}", - "xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithActivatingTitle": "创建并激活规则", - "xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithoutActivatingTitle": "创建规则但不激活", + "xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithActivatingTitle": "创建并激活规则", + "xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithoutActivatingTitle": "创建规则但不激活", "xpack.siem.detectionEngine.createRule.backToRulesDescription": "返回到信号检测规则", "xpack.siem.detectionEngine.createRule.editRuleButton": "编辑", "xpack.siem.detectionEngine.createRule.filtersLabel": "筛选", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 2f5172e8b386a4..265cfddab4c062 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -7,7 +7,7 @@ export { BASE_ALERT_API_PATH } from '../../../../alerting/common'; export { BASE_ACTION_API_PATH } from '../../../../actions/common'; -export const BASE_PATH = '/management/kibana/triggersActions'; +export const BASE_PATH = '/management/insightsAndAlerting/triggersActions'; export type Section = 'connectors' | 'alerts'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx index 4f5007949f8b14..1da9abea40dbac 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx @@ -127,7 +127,7 @@ describe('connector_add_flyout', () => { const manageLink = callout.find('EuiButton'); expect(manageLink).toHaveLength(1); expect(manageLink.getElements()[0].props.href).toMatchInlineSnapshot( - `"/app/kibana#/management/elasticsearch/license_management/"` + `"/app/kibana#/management/stack/license_management/"` ); const subscriptionLink = callout.find('EuiButtonEmpty'); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index a72d8815c95b4d..a0e8f3583ac433 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -15,13 +15,15 @@ export { AlertAction, Alert, AlertTypeModel, - AlertTypeParamsExpressionProps, ActionType, + ActionTypeRegistryContract, + AlertTypeParamsExpressionProps, } from './types'; export { ConnectorAddFlyout, ConnectorEditFlyout, } from './application/sections/action_connector_form'; +export { loadActionTypes } from './application/lib/action_connector_api'; export function plugin(ctx: PluginInitializerContext) { return new Plugin(ctx); diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 99a3d65589e8e9..016b564c47d008 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -12,7 +12,7 @@ import { registerBuiltInAlertTypes } from './application/components/builtin_aler import { hasShowActionsCapability, hasShowAlertsCapability } from './application/lib/capabilities'; import { ActionTypeModel, AlertTypeModel } from './types'; import { TypeRegistry } from './application/type_registry'; -import { ManagementStart } from '../../../../src/plugins/management/public'; +import { ManagementStart, ManagementSectionId } from '../../../../src/plugins/management/public'; import { boot } from './application/boot'; import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; import { PluginStartContract as AlertingStart } from '../../alerting/public'; @@ -73,12 +73,12 @@ export class Plugin // Don't register routes when user doesn't have access to the application if (canShowActions || canShowAlerts) { - plugins.management.sections.getSection('kibana')!.registerApp({ + plugins.management.sections.getSection(ManagementSectionId.InsightsAndAlerting).registerApp({ id: 'triggersActions', title: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', { defaultMessage: 'Alerts and Actions', }), - order: 7, + order: 0, mount: params => { boot({ dataPlugin: plugins.data, diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index bb0f21062c1516..be00a030d5a27c 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; import { CloudSetup } from '../../cloud/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { ManagementSetup, ManagementSectionId } from '../../../../src/plugins/management/public'; import { NEXT_MAJOR_VERSION } from '../common/version'; import { Config } from '../common/config'; @@ -24,7 +24,7 @@ export class UpgradeAssistantUIPlugin implements Plugin { if (!enabled) { return; } - const appRegistrar = management.sections.getSection('elasticsearch')!; + const appRegistrar = management.sections.getSection(ManagementSectionId.Stack); const isCloudEnabled = Boolean(cloud?.isCloudEnabled); appRegistrar.registerApp({ @@ -33,7 +33,7 @@ export class UpgradeAssistantUIPlugin implements Plugin { defaultMessage: '{version} Upgrade Assistant', values: { version: `${NEXT_MAJOR_VERSION}.0` }, }), - order: 1000, + order: 1, async mount(params) { const { mountManagementSection } = await import('./application/mount_management_section'); return mountManagementSection(coreSetup, isCloudEnabled, params); diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap index fb40a42e47f758..5ae90a1613575e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap @@ -28,7 +28,7 @@ Array [

@@ -64,7 +64,7 @@ exports[`ShowLicenseInfo shallow renders without errors 1`] = `

Start free 14-day trial diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap index 3ee949b9712bd4..df9de8c2ad03d8 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap @@ -160,7 +160,7 @@ exports[`ML Flyout component shows license info if no ml available 1`] = `

diff --git a/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx index fae81177a728c6..33fbbb117a11aa 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx @@ -22,7 +22,7 @@ export const ShowLicenseInfo = () => {

{labels.START_TRAIL_DESC}

{labels.START_TRAIL} diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index cba96cd2fe5b12..0cdb3c0feb71f6 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -61,7 +61,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ { get: (payload: Payload) => Action; diff --git a/x-pack/plugins/uptime/public/state/actions/utils.ts b/x-pack/plugins/uptime/public/state/actions/utils.ts index 8ce4cf011406be..5fb2b37298df60 100644 --- a/x-pack/plugins/uptime/public/state/actions/utils.ts +++ b/x-pack/plugins/uptime/public/state/actions/utils.ts @@ -5,8 +5,8 @@ */ import { createAction } from 'redux-actions'; +import { IHttpFetchError } from 'src/core/public'; import { AsyncAction, AsyncAction1 } from './types'; -import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; export function createAsyncAction( actionStr: string diff --git a/x-pack/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts index acd9bec5a74bcb..f2efd2ecb875c9 100644 --- a/x-pack/plugins/uptime/public/state/api/utils.ts +++ b/x-pack/plugins/uptime/public/state/api/utils.ts @@ -6,7 +6,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import { isRight } from 'fp-ts/lib/Either'; -import { HttpFetchQuery, HttpSetup } from '../../../../../../target/types/core/public'; +import { HttpFetchQuery, HttpSetup } from 'src/core/public'; class ApiService { private static instance: ApiService; diff --git a/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts index 0aa85609fe4f06..6535001cfc5efa 100644 --- a/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts @@ -6,7 +6,7 @@ import { call, put } from 'redux-saga/effects'; import { Action } from 'redux-actions'; -import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from 'src/core/public'; /** * Factory function for a fetch effect. It expects three action creators, diff --git a/x-pack/plugins/uptime/public/state/reducers/types.ts b/x-pack/plugins/uptime/public/state/reducers/types.ts index c81ee6875f305a..885296c0928acb 100644 --- a/x-pack/plugins/uptime/public/state/reducers/types.ts +++ b/x-pack/plugins/uptime/public/state/reducers/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from 'src/core/public'; export interface AsyncInitialState { data: ReduceStateType | null; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts index a0327c6dfa1dbf..787c9e07c0eb95 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts @@ -131,7 +131,7 @@ describe('', () => { expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).length).toBe(1); expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).props().href).toEqual( - `#/management/elasticsearch/watcher/watches/watch/${watch1.id}/status` + `#/management/insightsAndAlerting/watcher/watches/watch/${watch1.id}/status` ); }); diff --git a/x-pack/plugins/watcher/public/application/app.tsx b/x-pack/plugins/watcher/public/application/app.tsx index f4b94417193869..8a6d2746237e93 100644 --- a/x-pack/plugins/watcher/public/application/app.tsx +++ b/x-pack/plugins/watcher/public/application/app.tsx @@ -69,7 +69,7 @@ export const App = (deps: AppDeps) => { iconType="help" > {message}{' '} - + { values={{ watchName: watch.name, watchStatusLink: ( - + { return ( {id} @@ -326,7 +326,7 @@ export const WatchList = () => { )} iconType="pencil" color="primary" - href={`#/management/elasticsearch/watcher/watches/watch/${watch.id}/edit`} + href={`#/management/insightsAndAlerting/watcher/watches/watch/${watch.id}/edit`} data-test-subj="editWatchButton" /> diff --git a/x-pack/plugins/watcher/public/application/shared_imports.ts b/x-pack/plugins/watcher/public/application/shared_imports.ts index 94ef7af1c28d13..a9e07b80a9b221 100644 --- a/x-pack/plugins/watcher/public/application/shared_imports.ts +++ b/x-pack/plugins/watcher/public/application/shared_imports.ts @@ -10,6 +10,6 @@ export { UseRequestConfig, sendRequest, useRequest, -} from '../../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; +} from '../../../../../src/plugins/es_ui_shared/public/'; export { useXJsonMode } from '../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 6de21bc27d48c7..6496c742fcb403 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart } from 'kibana/public'; import { first, map, skip } from 'rxjs/operators'; +import { ManagementSectionId } from '../../../../src/plugins/management/public'; import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { LicenseStatus } from '../common/types/license_status'; @@ -28,14 +29,15 @@ export class WatcherUIPlugin implements Plugin { { notifications, http, uiSettings, getStartServices }: CoreSetup, { licensing, management, data, home, charts }: Dependencies ) { - const esSection = management.sections.getSection('elasticsearch'); + const esSection = management.sections.getSection(ManagementSectionId.InsightsAndAlerting); - const watcherESApp = esSection!.registerApp({ + const watcherESApp = esSection.registerApp({ id: 'watcher', title: i18n.translate( 'xpack.watcher.sections.watchList.managementSection.watcherDisplayName', { defaultMessage: 'Watcher' } ), + order: 3, mount: async ({ element, setBreadcrumbs }) => { const [core] = await getStartServices(); const { i18n: i18nDep, docLinks, savedObjects } = core; @@ -74,7 +76,7 @@ export class WatcherUIPlugin implements Plugin { defaultMessage: 'Detect changes in your data by creating, managing, and monitoring alerts.', }), icon: 'watchesApp', - path: '/app/kibana#/management/elasticsearch/watcher/watches', + path: '/app/kibana#/management/insightsAndAlerting/watcher/watches', showOnHomePage: false, }; diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index 6a2e3b2e596b68..3f2891f919e377 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -12,6 +12,7 @@ declare module 'kibana/server' { import { CoreSetup, + ICustomClusterClient, IScopedClusterClient, Logger, Plugin, @@ -33,8 +34,15 @@ export interface WatcherContext { client: IScopedClusterClient; } +async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { + const [core] = await getStartServices(); + const esConfig = { plugins: [elasticsearchJsPlugin] }; + return core.elasticsearch.legacy.createClient('watcher', esConfig); +} + export class WatcherServerPlugin implements Plugin { - log: Logger; + private readonly log: Logger; + private watcherESClient?: ICustomClusterClient; private licenseStatus: LicenseStatus = { hasRequired: false, @@ -44,21 +52,17 @@ export class WatcherServerPlugin implements Plugin { this.log = ctx.logger.get(); } - async setup( - { http, elasticsearch: elasticsearchService }: CoreSetup, - { licensing }: Dependencies - ) { + async setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies) { const router = http.createRouter(); const routeDependencies: RouteDependencies = { router, getLicenseStatus: () => this.licenseStatus, }; - const config = { plugins: [elasticsearchJsPlugin] }; - const watcherESClient = elasticsearchService.createClient('watcher', config); - http.registerRouteHandlerContext('watcher', (ctx, request) => { + http.registerRouteHandlerContext('watcher', async (ctx, request) => { + this.watcherESClient = this.watcherESClient ?? (await getCustomEsClient(getStartServices)); return { - client: watcherESClient.asScoped(request), + client: this.watcherESClient.asScoped(request), }; }); @@ -89,6 +93,12 @@ export class WatcherServerPlugin implements Plugin { } }); } + start() {} - stop() {} + + stop() { + if (this.watcherESClient) { + this.watcherESClient.close(); + } + } } diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 8ea554c42d6e0d..b00150467de00c 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -9,7 +9,8 @@ const alwaysImportedTests = [ require.resolve('../test/functional_endpoint_ingest_failure/config.ts'), require.resolve('../test/functional_endpoint/config.ts'), require.resolve('../test/functional_with_es_ssl/config.ts'), - require.resolve('../test/functional/config_security_basic.js'), + require.resolve('../test/functional/config_security_basic.ts'), + require.resolve('../test/functional/config_security_trial.ts'), require.resolve('../test/plugin_functional/config.ts'), ]; const onlyNotInCoverageTests = [ diff --git a/x-pack/tasks/build.ts b/x-pack/tasks/build.ts index 38ebb0bad4bdc7..8dbf6d212d5d29 100644 --- a/x-pack/tasks/build.ts +++ b/x-pack/tasks/build.ts @@ -8,7 +8,7 @@ import execa from 'execa'; import { resolve } from 'path'; import { writeFileSync } from 'fs'; -import pluginHelpers from '@kbn/plugin-helpers'; +import * as pluginHelpers from '@kbn/plugin-helpers'; import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; import gulp from 'gulp'; import del from 'del'; diff --git a/x-pack/tasks/dev.ts b/x-pack/tasks/dev.ts index 6e398f231a27c4..f43b67e2885612 100644 --- a/x-pack/tasks/dev.ts +++ b/x-pack/tasks/dev.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import pluginHelpers from '@kbn/plugin-helpers'; +import * as pluginHelpers from '@kbn/plugin-helpers'; import gulp from 'gulp'; import { prepareTask } from './prepare'; diff --git a/x-pack/tasks/test.ts b/x-pack/tasks/test.ts index 78a04413daae74..0d990bff9f44e4 100644 --- a/x-pack/tasks/test.ts +++ b/x-pack/tasks/test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import pluginHelpers from '@kbn/plugin-helpers'; +import * as pluginHelpers from '@kbn/plugin-helpers'; import gulp from 'gulp'; import { getEnabledPlugins } from './helpers/flags'; diff --git a/x-pack/test/api_integration/apis/management/index_management/indices.js b/x-pack/test/api_integration/apis/management/index_management/indices.js index ac50a20b02e3ba..5d523262d7f104 100644 --- a/x-pack/test/api_integration/apis/management/index_management/indices.js +++ b/x-pack/test/api_integration/apis/management/index_management/indices.js @@ -184,6 +184,7 @@ export default function({ getService }) { const { body } = await list().expect(200); const expectedKeys = [ 'health', + 'hidden', 'status', 'name', 'uuid', @@ -214,6 +215,7 @@ export default function({ getService }) { const { body } = await reload().expect(200); const expectedKeys = [ 'health', + 'hidden', 'status', 'name', 'uuid', diff --git a/x-pack/test/case_api_integration/basic/config.ts b/x-pack/test/case_api_integration/basic/config.ts index f9c248ec3d56f5..e711560e110976 100644 --- a/x-pack/test/case_api_integration/basic/config.ts +++ b/x-pack/test/case_api_integration/basic/config.ts @@ -9,6 +9,6 @@ import { createTestConfig } from '../common/config'; // eslint-disable-next-line import/no-default-export export default createTestConfig('basic', { disabledPlugins: [], - license: 'basic', + license: 'trial', ssl: true, }); diff --git a/x-pack/test/case_api_integration/basic/tests/cases/push_case.ts b/x-pack/test/case_api_integration/basic/tests/cases/push_case.ts index 848b980dee769e..2c1c4369e3ccd7 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/push_case.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/push_case.ts @@ -6,6 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { ObjectRemover as ActionsRemover } from '../../../../alerting_api_integration/common/lib'; import { CASE_CONFIGURE_URL, CASES_URL } from '../../../../../plugins/case/common/constants'; import { postCaseReq, defaultUser, postCommentReq } from '../../../common/lib/mock'; @@ -15,6 +16,7 @@ import { deleteComments, deleteConfiguration, getConfiguration, + getConnector, } from '../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -23,19 +25,31 @@ export default ({ getService }: FtrProviderContext): void => { const es = getService('es'); describe('push_case', () => { + const actionsRemover = new ActionsRemover(supertest); + afterEach(async () => { await deleteCases(es); await deleteComments(es); await deleteConfiguration(es); await deleteCasesUserActions(es); + await actionsRemover.removeAll(); }); it('should push a case', async () => { + const { body: connector } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'true') + .send(getConnector()) + .expect(200); + + actionsRemover.add('default', connector.id, 'action'); + const { body: configure } = await supertest .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') - .send(getConfiguration()) + .send(getConfiguration(connector.id)) .expect(200); + const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -58,11 +72,20 @@ export default ({ getService }: FtrProviderContext): void => { }); it('pushes a comment appropriately', async () => { + const { body: connector } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'true') + .send(getConnector()) + .expect(200); + + actionsRemover.add('default', connector.id, 'action'); + const { body: configure } = await supertest .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') - .send(getConfiguration()) + .send(getConfiguration(connector.id)) .expect(200); + const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -99,6 +122,7 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); expect(body.comments[0].pushed_by).to.eql(defaultUser); }); + it('unhappy path - 404s when case does not exist', async () => { await supertest .post(`${CASES_URL}/fake-id/_push`) diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 862705ab9610b6..8df4ff66c2a2ad 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -24,6 +24,7 @@ const enabledActionTypes = [ '.pagerduty', '.server-log', '.servicenow', + '.jira', '.slack', '.webhook', 'test.authorization', diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 4b1dc6ffa58915..5861db2eb8e5be 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -23,6 +23,37 @@ export const getConfigurationOutput = (update = false): Partial ({ + name: 'ServiceNow Connector', + actionTypeId: '.servicenow', + secrets: { + username: 'admin', + password: 'password', + }, + config: { + apiUrl: 'http://some.non.existent.com', + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'append', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, + ], + }, + }, +}); + export const removeServerGeneratedPropertiesFromConfigure = ( config: Partial ): Partial => { diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 082008bccddd13..5dd9364c48fda7 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -72,7 +72,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { .perform(); } - describe('lens smokescreen tests', () => { + // Failing: https://github.com/elastic/kibana/issues/66779 + describe.skip('lens smokescreen tests', () => { it('should allow editing saved visualizations', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); diff --git a/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts b/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts index d9d387f89640af..8c96fdbd0cbef5 100644 --- a/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts +++ b/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts @@ -21,7 +21,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await security.testUser.setRoles(['kibana_admin', 'reporting_user']); await esArchiver.load('empty_kibana'); await esArchiver.load('reporting/archived_reports'); - await pageObjects.common.navigateToActualUrl('kibana', '/management/kibana/reporting'); + await pageObjects.common.navigateToApp('reporting'); await testSubjects.existOrFail('reportJobListing', { timeout: 200000 }); }); diff --git a/x-pack/test/functional/apps/security/security.ts b/x-pack/test/functional/apps/security/security.ts index 3447f77aa7fd61..5bf7bc85eca8be 100644 --- a/x-pack/test/functional/apps/security/security.ts +++ b/x-pack/test/functional/apps/security/security.ts @@ -67,7 +67,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('logging out of a non-default space redirects to the login page at the server root', async () => { - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); diff --git a/x-pack/test/functional/apps/security/trial_license/index.ts b/x-pack/test/functional/apps/security/trial_license/index.ts new file mode 100644 index 00000000000000..0109d01ed4cff7 --- /dev/null +++ b/x-pack/test/functional/apps/security/trial_license/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('security app - trial license', function() { + this.tags('ciGroup4'); + + loadTestFile(require.resolve('./login_selector')); + }); +} diff --git a/x-pack/test/functional/apps/security/trial_license/login_selector.ts b/x-pack/test/functional/apps/security/trial_license/login_selector.ts new file mode 100644 index 00000000000000..14f9ce99556dbb --- /dev/null +++ b/x-pack/test/functional/apps/security/trial_license/login_selector.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { parse } from 'url'; +import { USERS_PATH } from '../../../../../plugins/security/public/management/management_urls'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const PageObjects = getPageObjects(['security', 'common']); + + describe('Login Selector', function() { + this.tags('includeFirefox'); + + before(async () => { + await getService('esSupertest') + .post('/_security/role_mapping/saml1') + .send({ roles: ['superuser'], enabled: true, rules: { field: { 'realm.name': 'saml1' } } }) + .expect(200); + + await esArchiver.load('empty_kibana'); + await PageObjects.security.forceLogout(); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + beforeEach(async () => { + await browser.get(`${PageObjects.common.getHostPort()}/login`); + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + }); + + afterEach(async () => { + await PageObjects.security.forceLogout(); + }); + + it('can login with Login Form preserving original URL', async () => { + await PageObjects.common.navigateToActualUrl('kibana', USERS_PATH, { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }); + await PageObjects.common.waitUntilUrlIncludes('next='); + + await PageObjects.security.loginSelector.login('basic', 'basic1'); + + // We need to make sure that both path and hash are respected. + const currentURL = parse(await browser.getCurrentUrl()); + expect(currentURL.pathname).to.eql('/app/kibana'); + expect(currentURL.hash).to.eql(`#${USERS_PATH}`); + }); + + it('can login with SSO preserving original URL', async () => { + await PageObjects.common.navigateToActualUrl('kibana', USERS_PATH, { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }); + await PageObjects.common.waitUntilUrlIncludes('next='); + + await PageObjects.security.loginSelector.login('saml', 'saml1'); + + // We need to make sure that both path and hash are respected. + const currentURL = parse(await browser.getCurrentUrl()); + expect(currentURL.pathname).to.eql('/app/kibana'); + expect(currentURL.hash).to.eql(`#${USERS_PATH}`); + }); + + it('should show toast with error if SSO fails', async () => { + await PageObjects.security.loginSelector.selectLoginMethod('saml', 'unknown_saml'); + + const toastTitle = await PageObjects.common.closeToast(); + expect(toastTitle).to.eql('Could not perform login.'); + + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + }); + + it('can go to Login Form and return back to Selector', async () => { + await PageObjects.security.loginSelector.selectLoginMethod('basic', 'basic1'); + await PageObjects.security.loginSelector.verifyLoginFormIsVisible(); + + await testSubjects.click('loginBackToSelector'); + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + + await PageObjects.security.loginSelector.login('saml', 'saml1'); + }); + + it('can show Login Help from both Login Selector and Login Form', async () => { + // Show Login Help from Login Selector. + await testSubjects.click('loginHelpLink'); + await PageObjects.security.loginSelector.verifyLoginHelpIsVisible('Some-login-help.'); + + // Go back to Login Selector. + await testSubjects.click('loginBackToLoginLink'); + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + + // Go to Login Form and show Login Help there. + await PageObjects.security.loginSelector.selectLoginMethod('basic', 'basic1'); + await PageObjects.security.loginSelector.verifyLoginFormIsVisible(); + await testSubjects.click('loginHelpLink'); + await PageObjects.security.loginSelector.verifyLoginHelpIsVisible('Some-login-help.'); + + // Go back to Login Form. + await testSubjects.click('loginBackToLoginLink'); + await PageObjects.security.loginSelector.verifyLoginFormIsVisible(); + + // Go back to Login Selector and show Login Help there again. + await testSubjects.click('loginBackToSelector'); + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + await testSubjects.click('loginHelpLink'); + await PageObjects.security.loginSelector.verifyLoginHelpIsVisible('Some-login-help.'); + + // Go back to Login Selector. + await testSubjects.click('loginBackToLoginLink'); + await PageObjects.security.loginSelector.verifyLoginSelectorIsVisible(); + }); + }); +} diff --git a/x-pack/test/functional/apps/spaces/copy_saved_objects.ts b/x-pack/test/functional/apps/spaces/copy_saved_objects.ts index 0b5718e92e38e7..0f9fa2aed164ac 100644 --- a/x-pack/test/functional/apps/spaces/copy_saved_objects.ts +++ b/x-pack/test/functional/apps/spaces/copy_saved_objects.ts @@ -32,7 +32,7 @@ export default function spaceSelectorFunctonalTests({ disabledFeatures: [], }); - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); diff --git a/x-pack/test/functional/apps/spaces/enter_space.ts b/x-pack/test/functional/apps/spaces/enter_space.ts index d45b8a1ea4cdb7..77d2db6c00c91e 100644 --- a/x-pack/test/functional/apps/spaces/enter_space.ts +++ b/x-pack/test/functional/apps/spaces/enter_space.ts @@ -27,7 +27,7 @@ export default function enterSpaceFunctonalTests({ it('falls back to the default home page when the configured default route is malformed', async () => { const spaceId = 'default'; - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); @@ -39,7 +39,7 @@ export default function enterSpaceFunctonalTests({ it('allows user to navigate to different spaces, respecting the configured default route', async () => { const spaceId = 'another-space'; - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); diff --git a/x-pack/test/functional/apps/spaces/spaces_selection.ts b/x-pack/test/functional/apps/spaces/spaces_selection.ts index c3fb93f4e45726..b77b656f7af568 100644 --- a/x-pack/test/functional/apps/spaces/spaces_selection.ts +++ b/x-pack/test/functional/apps/spaces/spaces_selection.ts @@ -36,7 +36,7 @@ export default function spaceSelectorFunctonalTests({ it('allows user to navigate to different spaces', async () => { const spaceId = 'another-space'; - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); @@ -67,7 +67,7 @@ export default function spaceSelectorFunctonalTests({ before(async () => { await esArchiver.load('spaces/selector'); - await PageObjects.security.login(null, null, { + await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); await PageObjects.spaceSelector.clickSpaceCard('default'); diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index aac8e8d8ef5ad1..d94132efb1644f 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -409,10 +409,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.security.login( 'no_visualize_privileges_user', 'no_visualize_privileges_user-password', - { - expectSpaceSelector: false, - shouldLoginIfPrompted: false, - } + { expectSpaceSelector: false } ); }); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 0639782dc7f7b0..94976511a1b124 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -114,7 +114,7 @@ export default async function({ readConfigFile }) { }, logstashPipelines: { pathname: '/app/kibana', - hash: '/management/logstash/pipelines', + hash: '/management/ingest/pipelines', }, maps: { pathname: '/app/maps', @@ -155,7 +155,7 @@ export default async function({ readConfigFile }) { }, rollupJob: { pathname: '/app/kibana', - hash: '/management/elasticsearch/rollup_jobs/', + hash: '/management/data/rollup_jobs/', }, apiKeys: { pathname: '/app/kibana', @@ -163,46 +163,46 @@ export default async function({ readConfigFile }) { }, licenseManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/license_management', + hash: '/management/stack/license_management', }, indexManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/index_management', + hash: '/management/data/index_management', }, indexLifecycleManagement: { pathname: '/app/kibana', - hash: '/management/elasticsearch/index_lifecycle_management', + hash: '/management/data/index_lifecycle_management', }, ingestPipelines: { pathname: '/app/kibana', - hash: '/management/elasticsearch/ingest_pipelines', + hash: '/management/ingest/ingest_pipelines', }, snapshotRestore: { pathname: '/app/kibana', - hash: '/management/elasticsearch/snapshot_restore', + hash: '/management/data/snapshot_restore', }, crossClusterReplication: { pathname: '/app/kibana', - hash: '/management/elasticsearch/cross_cluster_replication', + hash: '/management/data/cross_cluster_replication', }, remoteClusters: { pathname: '/app/kibana', - hash: '/management/elasticsearch/remote_clusters', + hash: '/management/data/remote_clusters', }, apm: { pathname: '/app/apm', }, watcher: { pathname: '/app/kibana', - hash: '/management/elasticsearch/watcher/watches/', + hash: '/management/insightsAndAlerting/watcher/watches/', }, transform: { pathname: '/app/kibana/', - hash: '/management/elasticsearch/transform', + hash: '/management/data/transform', }, reporting: { pathname: '/app/kibana/', - hash: '/management/kibana/reporting', + hash: '/management/insightsAndAlerting/reporting', }, }, diff --git a/x-pack/test/functional/config_security_basic.js b/x-pack/test/functional/config_security_basic.ts similarity index 93% rename from x-pack/test/functional/config_security_basic.js rename to x-pack/test/functional/config_security_basic.ts index 2bb59796b55179..185c41c48e1151 100644 --- a/x-pack/test/functional/config_security_basic.js +++ b/x-pack/test/functional/config_security_basic.ts @@ -8,12 +8,13 @@ import { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; import { pageObjects } from './page_objects'; // the default export of config files must be a config provider // that returns an object with the projects config values -export default async function({ readConfigFile }) { +export default async function({ readConfigFile }: FtrConfigProviderContext) { const kibanaCommonConfig = await readConfigFile( require.resolve('../../../test/common/config.js') ); diff --git a/x-pack/test/functional/config_security_trial.ts b/x-pack/test/functional/config_security_trial.ts new file mode 100644 index 00000000000000..4a3e7858b7dd80 --- /dev/null +++ b/x-pack/test/functional/config_security_trial.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable import/no-default-export */ + +import { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +// the default export of config files must be a config provider +// that returns an object with the projects config values +export default async function({ readConfigFile }: FtrConfigProviderContext) { + const kibanaCommonConfig = await readConfigFile( + require.resolve('../../../test/common/config.js') + ); + const kibanaFunctionalConfig = await readConfigFile( + require.resolve('../../../test/functional/config.js') + ); + + const kibanaPort = kibanaFunctionalConfig.get('servers.kibana.port'); + const idpPath = resolve(__dirname, '../saml_api_integration/fixtures/saml_provider/metadata.xml'); + const samlIdPPlugin = resolve(__dirname, '../saml_api_integration/fixtures/saml_provider'); + + return { + testFiles: [resolve(__dirname, './apps/security/trial_license')], + + services, + pageObjects, + + servers: kibanaFunctionalConfig.get('servers'), + + esTestCluster: { + license: 'trial', + from: 'snapshot', + serverArgs: [ + 'xpack.security.authc.token.enabled=true', + 'xpack.security.authc.realms.saml.saml1.order=0', + `xpack.security.authc.realms.saml.saml1.idp.metadata.path=${idpPath}`, + 'xpack.security.authc.realms.saml.saml1.idp.entity_id=http://www.elastic.co/saml1', + `xpack.security.authc.realms.saml.saml1.sp.entity_id=http://localhost:${kibanaPort}`, + `xpack.security.authc.realms.saml.saml1.sp.logout=http://localhost:${kibanaPort}/logout`, + `xpack.security.authc.realms.saml.saml1.sp.acs=http://localhost:${kibanaPort}/api/security/saml/callback`, + 'xpack.security.authc.realms.saml.saml1.attributes.principal=urn:oid:0.0.7', + ], + }, + + kbnTestServer: { + ...kibanaCommonConfig.get('kbnTestServer'), + serverArgs: [ + ...kibanaCommonConfig.get('kbnTestServer.serverArgs'), + `--plugin-path=${samlIdPPlugin}`, + '--server.uuid=5b2de169-2785-441b-ae8c-186a1936b17d', + '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', + `--xpack.security.loginHelp="Some-login-help."`, + '--xpack.security.authc.providers.basic.basic1.order=0', + '--xpack.security.authc.providers.saml.saml1.order=1', + '--xpack.security.authc.providers.saml.saml1.realm=saml1', + '--xpack.security.authc.providers.saml.saml1.description="Log-in-with-SAML"', + '--xpack.security.authc.providers.saml.saml1.icon=logoKibana', + '--xpack.security.authc.providers.saml.unknown_saml.order=2', + '--xpack.security.authc.providers.saml.unknown_saml.realm=unknown_realm', + '--xpack.security.authc.providers.saml.unknown_saml.description="Do-not-log-in-with-THIS-SAML"', + '--xpack.security.authc.providers.saml.unknown_saml.icon=logoAWS', + ], + }, + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + }, + }, + apps: kibanaFunctionalConfig.get('apps'), + esArchiver: { directory: resolve(__dirname, 'es_archives') }, + screenshots: { directory: resolve(__dirname, 'screenshots') }, + + junit: { + reportName: 'Chrome X-Pack UI Functional Tests', + }, + }; +} diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index cb3598652a39ae..d313fd2046c03c 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -233,7 +233,7 @@ "title" : "document example top hits", "description" : "", "mapStateJSON" : "{\"zoom\":4.1,\"center\":{\"lon\":-100.61091,\"lat\":33.23887},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-24T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"topHitsTimeField\":\"@timestamp\",\"useTopHits\":true,\"topHitsSplitField\":\"machine.os.raw\",\"topHitsSize\":2,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "layerListJSON" : "[{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"topHitsTimeField\":\"@timestamp\",\"useTopHits\":true,\"topHitsSplitField\":\"machine.os.raw\",\"topHitsSize\":2,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", "uiStateJSON" : "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}", "bounds" : { "type" : "polygon", @@ -467,7 +467,7 @@ "type": "envelope" }, "description": "", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]", + "layerListJSON" : "[{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]", "mapStateJSON": "{\"zoom\":3.02,\"center\":{\"lon\":77.33426,\"lat\":-0.04647},\"timeFilters\":{\"from\":\"now-17m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "join example", "uiStateJSON": "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[\"n1t6f\"]}" @@ -512,7 +512,7 @@ ], "type": "envelope" }, - "layerListJSON": "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{},\"previousStyle\":null},\"type\":\"HEATMAP\"}]", + "layerListJSON": "[{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{},\"previousStyle\":null},\"type\":\"HEATMAP\"}]", "mapStateJSON": "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "geo grid heatmap example", "uiStateJSON": "{\"isDarkMode\":false}" @@ -543,7 +543,7 @@ "type": "envelope" }, "description": "", - "layerListJSON": "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "layerListJSON": "[{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]", "mapStateJSON": "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}", "title": "geo grid vector grid example", "uiStateJSON": "{\"isDarkMode\":false}" diff --git a/x-pack/test/functional/page_objects/monitoring_page.js b/x-pack/test/functional/page_objects/monitoring_page.js index 323c01e2348805..ece0c0a6c78543 100644 --- a/x-pack/test/functional/page_objects/monitoring_page.js +++ b/x-pack/test/functional/page_objects/monitoring_page.js @@ -5,7 +5,7 @@ */ export function MonitoringPageProvider({ getPageObjects, getService }) { - const PageObjects = getPageObjects(['common', 'header', 'security', 'shield', 'spaceSelector']); + const PageObjects = getPageObjects(['common', 'header', 'security', 'login', 'spaceSelector']); const testSubjects = getService('testSubjects'); const security = getService('security'); @@ -20,7 +20,7 @@ export function MonitoringPageProvider({ getPageObjects, getService }) { if (!useSuperUser) { await PageObjects.security.forceLogout(); - await PageObjects.shield.login('basic_monitoring_user', 'monitoring_user_password'); + await PageObjects.login.login('basic_monitoring_user', 'monitoring_user_password'); } await PageObjects.common.navigateToApp('monitoring'); } diff --git a/x-pack/test/functional/page_objects/security_page.js b/x-pack/test/functional/page_objects/security_page.ts similarity index 61% rename from x-pack/test/functional/page_objects/security_page.js rename to x-pack/test/functional/page_objects/security_page.ts index bddadcce12e1fe..d2462faa2c4f03 100644 --- a/x-pack/test/functional/page_objects/security_page.js +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { map as mapAsync } from 'bluebird'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { Role } from '../../../plugins/security/common/model'; -export function SecurityPageProvider({ getService, getPageObjects }) { +export function SecurityPageProvider({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const config = getService('config'); const retry = getService('retry'); @@ -17,50 +18,114 @@ export function SecurityPageProvider({ getService, getPageObjects }) { const userMenu = getService('userMenu'); const PageObjects = getPageObjects(['common', 'header', 'settings', 'home', 'error']); - class LoginPage { - async login(username, password, options = {}) { - const [superUsername, superPassword] = config.get('servers.elasticsearch.auth').split(':'); + interface LoginOptions { + expectSpaceSelector?: boolean; + expectSuccess?: boolean; + expectForbidden?: boolean; + } - username = username || superUsername; - password = password || superPassword; + type LoginExpectedResult = 'spaceSelector' | 'error' | 'chrome'; + + async function waitForLoginPage() { + log.debug('Waiting for Login Page to appear.'); + await retry.waitForWithTimeout('login page', config.get('timeouts.waitFor') * 5, async () => { + // As a part of the cleanup flow tests usually try to log users out, but there are cases when + // browser/Kibana would like users to confirm that they want to navigate away from the current + // page and lose the state (e.g. unsaved changes) via native alert dialog. + const alert = await browser.getAlert(); + if (alert && alert.accept) { + await alert.accept(); + } + return await find.existsByDisplayedByCssSelector('.login-form'); + }); + } - const expectSpaceSelector = options.expectSpaceSelector || false; - const expectSuccess = options.expectSuccess; - const expectForbidden = options.expectForbidden || false; - const rawDataTabLocator = 'a[id=rawdata-tab]'; + async function waitForLoginForm() { + log.debug('Waiting for Login Form to appear.'); + await retry.waitForWithTimeout('login form', config.get('timeouts.waitFor') * 5, async () => { + return await testSubjects.exists('loginForm'); + }); + } - await PageObjects.common.navigateToApp('login'); + async function waitForLoginSelector() { + log.debug('Waiting for Login Selector to appear.'); + await retry.waitForWithTimeout( + 'login selector', + config.get('timeouts.waitFor') * 5, + async () => { + return await testSubjects.exists('loginSelector'); + } + ); + } - await testSubjects.setValue('loginUsername', username); - await testSubjects.setValue('loginPassword', password); - await testSubjects.click('loginSubmit'); + async function waitForLoginHelp(helpText: string) { + log.debug(`Waiting for Login Help to appear with text: ${helpText}.`); + await retry.waitForWithTimeout('login help', config.get('timeouts.waitFor') * 5, async () => { + return (await testSubjects.getVisibleText('loginHelp')) === helpText; + }); + } + + async function waitForLoginResult(expectedResult?: LoginExpectedResult) { + log.debug(`Waiting for login result, expected: ${expectedResult}.`); + + // wait for either space selector, kibanaChrome or loginErrorMessage + if (expectedResult === 'spaceSelector') { + await retry.try(() => testSubjects.find('kibanaSpaceSelector')); + log.debug( + `Finished login process, landed on space selector. currentUrl = ${await browser.getCurrentUrl()}` + ); + return; + } - // wait for either space selector, kibanaChrome or loginErrorMessage - if (expectSpaceSelector) { - await retry.try(() => testSubjects.find('kibanaSpaceSelector')); - log.debug( - `Finished login process, landed on space selector. currentUrl = ${await browser.getCurrentUrl()}` - ); - } else if (expectForbidden) { + if (expectedResult === 'error') { + const rawDataTabLocator = 'a[id=rawdata-tab]'; + if (await find.existsByCssSelector(rawDataTabLocator)) { + // Firefox has 3 tabs and requires navigation to see Raw output + await find.clickByCssSelector(rawDataTabLocator); + } + await retry.try(async () => { if (await find.existsByCssSelector(rawDataTabLocator)) { - // Firefox has 3 tabs and requires navigation to see Raw output await find.clickByCssSelector(rawDataTabLocator); } - await retry.try(async () => { - if (await find.existsByCssSelector(rawDataTabLocator)) { - await find.clickByCssSelector(rawDataTabLocator); - } - await PageObjects.error.expectForbidden(); - }); - log.debug( - `Finished login process, found forbidden message. currentUrl = ${await browser.getCurrentUrl()}` - ); - } else if (expectSuccess) { - await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000); - log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`); - } + await PageObjects.error.expectForbidden(); + }); + log.debug( + `Finished login process, found forbidden message. currentUrl = ${await browser.getCurrentUrl()}` + ); + return; } + if (expectedResult === 'chrome') { + await find.byCssSelector( + '[data-test-subj="kibanaChrome"] .app-wrapper:not(.hidden-chrome)', + 20000 + ); + log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`); + } + } + + const loginPage = Object.freeze({ + async login(username?: string, password?: string, options: LoginOptions = {}) { + await PageObjects.common.navigateToApp('login'); + + await waitForLoginForm(); + + const [superUsername, superPassword] = config.get('servers.elasticsearch.auth').split(':'); + await testSubjects.setValue('loginUsername', username || superUsername); + await testSubjects.setValue('loginPassword', password || superPassword); + await testSubjects.click('loginSubmit'); + + await waitForLoginResult( + options.expectSpaceSelector + ? 'spaceSelector' + : options.expectForbidden + ? 'error' + : options.expectSuccess + ? 'chrome' + : undefined + ); + }, + async getErrorMessage() { return await retry.try(async () => { const errorMessageContainer = await retry.try(() => testSubjects.find('loginErrorMessage')); @@ -72,13 +137,53 @@ export function SecurityPageProvider({ getService, getPageObjects }) { return errorMessageText; }); - } - } + }, + }); + + const loginSelector = Object.freeze({ + async login(providerType: string, providerName: string, options?: Record) { + log.debug(`Starting login flow for ${providerType}/${providerName}`); + + await this.verifyLoginSelectorIsVisible(); + await this.selectLoginMethod(providerType, providerName); + + if (providerType === 'basic' || providerType === 'token') { + await waitForLoginForm(); + + const [superUsername, superPassword] = config.get('servers.elasticsearch.auth').split(':'); + await testSubjects.setValue('loginUsername', options?.username ?? superUsername); + await testSubjects.setValue('loginPassword', options?.password ?? superPassword); + await testSubjects.click('loginSubmit'); + } + + await waitForLoginResult('chrome'); + + log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`); + }, + + async selectLoginMethod(providerType: string, providerName: string) { + // Ensure welcome screen won't be shown. This is relevant for environments which don't allow + // to use the yml setting, e.g. cloud. + await browser.setLocalStorageItem('home:welcome:show', 'false'); + await testSubjects.click(`loginCard-${providerType}/${providerName}`); + }, + + async verifyLoginFormIsVisible() { + await waitForLoginForm(); + }, + + async verifyLoginSelectorIsVisible() { + await waitForLoginSelector(); + }, + + async verifyLoginHelpIsVisible(helpText: string) { + await waitForLoginHelp(helpText); + }, + }); class SecurityPage { - constructor() { - this.loginPage = new LoginPage(); - } + public loginPage = loginPage; + public loginSelector = loginSelector; async initTests() { log.debug('SecurityPage:initTests'); @@ -87,7 +192,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { await browser.setWindowSize(1600, 1000); } - async login(username, password, options = {}) { + async login(username?: string, password?: string, options: LoginOptions = {}) { await this.loginPage.login(username, password, options); if (options.expectSpaceSelector || options.expectForbidden) { @@ -106,7 +211,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { } await userMenu.clickLogoutButton(); - await this.waitForLoginForm(); + await waitForLoginPage(); } async forceLogout() { @@ -120,17 +225,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { const url = PageObjects.common.getHostPort() + '/logout'; await browser.get(url); log.debug('Waiting on the login form to appear'); - await this.waitForLoginForm(); - } - - async waitForLoginForm() { - await retry.waitForWithTimeout('login form', config.get('timeouts.waitFor') * 5, async () => { - const alert = await browser.getAlert(); - if (alert && alert.accept) { - await alert.accept(); - } - return await find.existsByDisplayedByCssSelector('.login-form'); - }); + await waitForLoginPage(); } async clickRolesSection() { @@ -149,14 +244,10 @@ export function SecurityPageProvider({ getService, getPageObjects }) { await retry.try(() => testSubjects.click('createRoleButton')); } - async clickCloneRole(roleName) { + async clickCloneRole(roleName: string) { await retry.try(() => testSubjects.click(`clone-role-action-${roleName}`)); } - async getCreateIndexPatternInputFieldExists() { - return await testSubjects.exists('createIndexPatternNameInput'); - } - async clickCancelEditUser() { await testSubjects.click('userFormCancelButton'); } @@ -177,7 +268,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { await PageObjects.header.waitUntilLoadingHasFinished(); } - async addIndexToRole(index) { + async addIndexToRole(index: string) { log.debug(`Adding index ${index} to role`); const indexInput = await retry.try(() => find.byCssSelector('[data-test-subj="indicesInput0"] input') @@ -186,7 +277,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { await indexInput.type('\n'); } - async addPrivilegeToRole(privilege) { + async addPrivilegeToRole(privilege: string) { log.debug(`Adding privilege ${privilege} to role`); const privilegeInput = await retry.try(() => find.byCssSelector('[data-test-subj="privilegesInput0"] input') @@ -204,7 +295,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { // await options.click(); } - async assignRoleToUser(role) { + async assignRoleToUser(role: string) { await this.selectRole(role); } @@ -223,8 +314,8 @@ export function SecurityPageProvider({ getService, getPageObjects }) { } async getElasticsearchUsers() { - const users = await testSubjects.findAll('userRow'); - return mapAsync(users, async user => { + const users = []; + for (const user of await testSubjects.findAll('userRow')) { const fullnameElement = await user.findByTestSubject('userRowFullName'); const usernameElement = await user.findByTestSubject('userRowUserName'); const emailElement = await user.findByTestSubject('userRowEmail'); @@ -233,20 +324,22 @@ export function SecurityPageProvider({ getService, getPageObjects }) { const isUserReserved = (await user.findAllByTestSubject('userReserved', 1)).length > 0; const isUserDeprecated = (await user.findAllByTestSubject('userDeprecated', 1)).length > 0; - return { + users.push({ username: await usernameElement.getVisibleText(), fullname: await fullnameElement.getVisibleText(), email: await emailElement.getVisibleText(), roles: (await rolesElement.getVisibleText()).split('\n').map(role => role.trim()), reserved: isUserReserved, deprecated: isUserDeprecated, - }; - }); + }); + } + + return users; } async getElasticsearchRoles() { - const users = await testSubjects.findAll('roleRow'); - return mapAsync(users, async role => { + const roles = []; + for (const role of await testSubjects.findAll('roleRow')) { const [rolename, reserved, deprecated] = await Promise.all([ role.findByTestSubject('roleRowName').then(el => el.getVisibleText()), // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases @@ -255,12 +348,10 @@ export function SecurityPageProvider({ getService, getPageObjects }) { role.findAllByTestSubject('roleDeprecated', 1).then(el => el.length > 0), ]); - return { - rolename, - reserved, - deprecated, - }; - }); + roles.push({ rolename, reserved, deprecated }); + } + + return roles; } async clickNewUser() { @@ -271,7 +362,15 @@ export function SecurityPageProvider({ getService, getPageObjects }) { return await testSubjects.click('createRoleButton'); } - async addUser(userObj) { + async addUser(userObj: { + username: string; + password: string; + confirmPassword: string; + email: string; + fullname: string; + roles: string[]; + save?: boolean; + }) { const self = this; await this.clickNewUser(); log.debug('username = ' + userObj.username); @@ -298,35 +397,36 @@ export function SecurityPageProvider({ getService, getPageObjects }) { } } - addRole(roleName, userObj) { + addRole(roleName: string, roleObj: Role) { const self = this; return ( this.clickNewRole() .then(function() { // We have to use non-test-subject selectors because this markup is generated by ui-select. - log.debug('userObj.indices[0].names = ' + userObj.elasticsearch.indices[0].names); + log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names); return testSubjects.append('roleFormNameInput', roleName); }) .then(function() { return find.setValue( '[data-test-subj="indicesInput0"] input', - userObj.elasticsearch.indices[0].names + '\n' + roleObj.elasticsearch.indices[0].names + '\n' ); }) .then(function() { return testSubjects.click('restrictDocumentsQuery0'); }) .then(function() { - if (userObj.elasticsearch.indices[0].query) { - return testSubjects.setValue('queryInput0', userObj.elasticsearch.indices[0].query); + if (roleObj.elasticsearch.indices[0].query) { + return testSubjects.setValue('queryInput0', roleObj.elasticsearch.indices[0].query); } }) - //KibanaPriv - .then(function() { - function addKibanaPriv(priv) { - return priv.reduce(async function(promise, privName) { + // KibanaPrivilege + .then(async () => { + const globalPrivileges = (roleObj.kibana as any).global; + if (globalPrivileges) { + for (const privilegeName of globalPrivileges) { const button = await testSubjects.find('addSpacePrivilegeButton'); await button.click(); @@ -339,33 +439,30 @@ export function SecurityPageProvider({ getService, getPageObjects }) { const basePrivilegeSelector = await testSubjects.find('basePrivilegeComboBox'); await basePrivilegeSelector.click(); - const privilegeOption = await find.byCssSelector(`#basePrivilege_${privName}`); + const privilegeOption = await find.byCssSelector(`#basePrivilege_${privilegeName}`); await privilegeOption.click(); const createPrivilegeButton = await testSubjects.find('createSpacePrivilegeButton'); await createPrivilegeButton.click(); - - return promise; - }, Promise.resolve()); + } } - return userObj.kibana.global ? addKibanaPriv(userObj.kibana.global) : Promise.resolve(); }) .then(function() { - function addPriv(priv) { - return priv.reduce(function(promise, privName) { + function addPrivilege(privileges: string[]) { + return privileges.reduce(function(promise: Promise, privilegeName: string) { // We have to use non-test-subject selectors because this markup is generated by ui-select. return promise - .then(() => self.addPrivilegeToRole(privName)) + .then(() => self.addPrivilegeToRole(privilegeName)) .then(() => PageObjects.common.sleep(250)); }, Promise.resolve()); } - return addPriv(userObj.elasticsearch.indices[0].privileges); + return addPrivilege(roleObj.elasticsearch.indices[0].privileges); }) - //clicking the Granted fields and removing the asterix + // clicking the Granted fields and removing the asterix .then(async function() { - function addGrantedField(field) { - return field.reduce(function(promise, fieldName) { + function addGrantedField(field: string[]) { + return field.reduce(function(promise: Promise, fieldName: string) { return promise .then(function() { return find.setValue('[data-test-subj="fieldInput0"] input', fieldName + '\n'); @@ -376,7 +473,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { }, Promise.resolve()); } - if (userObj.elasticsearch.indices[0].field_security) { + if (roleObj.elasticsearch.indices[0].field_security) { // Toggle FLS switch await testSubjects.click('restrictFieldsQuery0'); @@ -386,10 +483,10 @@ export function SecurityPageProvider({ getService, getPageObjects }) { 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon' ) .then(function() { - return addGrantedField(userObj.elasticsearch.indices[0].field_security.grant); + return addGrantedField(roleObj.elasticsearch.indices[0].field_security!.grant!); }); } - }) //clicking save button + }) // clicking save button .then(async () => { log.debug('click save button'); await testSubjects.click('roleFormSaveButton'); @@ -400,7 +497,7 @@ export function SecurityPageProvider({ getService, getPageObjects }) { ); } - async selectRole(role) { + async selectRole(role: string) { const dropdown = await testSubjects.find('rolesDropdown'); const input = await dropdown.findByCssSelector('input'); await input.type(role); @@ -409,8 +506,8 @@ export function SecurityPageProvider({ getService, getPageObjects }) { await testSubjects.find(`roleOption-${role}`); } - deleteUser(username) { - let alertText; + deleteUser(username: string) { + let alertText: string; log.debug('Delete user ' + username); return find .clickByDisplayedLinkText(username) @@ -436,11 +533,6 @@ export function SecurityPageProvider({ getService, getPageObjects }) { return alertText; }); } - - async getPermissionDeniedMessage() { - const el = await find.displayedByCssSelector('span.kuiInfoPanelHeader__title'); - return await el.getVisibleText(); - } } return new SecurityPage(); } diff --git a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts index 49f9b01c968958..d67f6bc946df25 100644 --- a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts @@ -287,10 +287,16 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async setModelMemory(modelMemory: string) { - await mlCommon.setValueWithChecks('mlAnalyticsCreateJobFlyoutModelMemoryInput', modelMemory, { - clearWithKeyboard: true, + await retry.tryForTime(15 * 1000, async () => { + await mlCommon.setValueWithChecks( + 'mlAnalyticsCreateJobFlyoutModelMemoryInput', + modelMemory, + { + clearWithKeyboard: true, + } + ); + await this.assertModelMemoryValue(modelMemory); }); - await this.assertModelMemoryValue(modelMemory); }, async assertCreateIndexPatternSwitchExists() { diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts index 37cc71d6865b02..d372bd53c081b9 100644 --- a/x-pack/test/functional/services/uptime/navigation.ts +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -65,8 +65,8 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv }, goToCertificates: async () => { - await testSubjects.click('uptimeCertificatesLink', 10000); - return retry.tryForTime(60 * 1000, async () => { + return retry.try(async () => { + await testSubjects.click('uptimeCertificatesLink'); await testSubjects.existOrFail('uptimeCertificatesPage'); }); }, diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 50de76d67e06b5..f69c17c96f0741 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -51,7 +51,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { ...xpackFunctionalConfig.get('apps'), triggersActions: { pathname: '/app/kibana', - hash: '/management/kibana/triggersActions', + hash: '/management/insightsAndAlerting/triggersActions', }, }, esTestCluster: { diff --git a/x-pack/test/saml_api_integration/fixtures/saml_provider/kibana.json b/x-pack/test/saml_api_integration/fixtures/saml_provider/kibana.json new file mode 100644 index 00000000000000..3cbd37e38bb2da --- /dev/null +++ b/x-pack/test/saml_api_integration/fixtures/saml_provider/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "saml_provider_plugin", + "version": "8.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": false +} diff --git a/x-pack/test/saml_api_integration/fixtures/saml_provider/metadata.xml b/x-pack/test/saml_api_integration/fixtures/saml_provider/metadata.xml new file mode 100644 index 00000000000000..19a6c132641449 --- /dev/null +++ b/x-pack/test/saml_api_integration/fixtures/saml_provider/metadata.xml @@ -0,0 +1,41 @@ + + + + + + + + MIIDOTCCAiGgAwIBAgIVANNWkg9lzNiLqNkMFhFKHcXyaZmqMA0GCSqGSIb3DQEB +CwUAMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2Vu +ZXJhdGVkIENBMCAXDTE5MTIyNzE3MDM0MloYDzIwNjkxMjE0MTcwMzQyWjARMQ8w +DQYDVQQDEwZraWJhbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQ +wYYbQtbRBKJ4uNZc2+IgRU+7NNL21ZebQlEIMgK7jAqOMrsW2b5DATz41Fd+GQFU +FUYYjwo+PQj6sJHshOJo/gNb32HrydvMI7YPvevkszkuEGCfXxQ3Dw2RTACLgD0Q +OCkwHvn3TMf0loloV/ePGWaZDYZaXi3a5DdWi/HFFoJysgF0JV2f6XyKhJkGaEfJ +s9pWX269zH/XQvGNx4BEimJpYB8h4JnDYPFIiQdqj+sl2b+kS1hH9kL5gBAMXjFU +vcNnX+PmyTjyJrGo75k0ku+spBf1bMwuQt3uSmM+TQIXkvFDmS0DOVESrpA5EC1T +BUGRz6o/I88Xx4Mud771AgMBAAGjYzBhMB0GA1UdDgQWBBQLB1Eo23M3Ss8MsFaz +V+Twcb3PmDAfBgNVHSMEGDAWgBQa7SYOe8NGcF00EbwPHA91YCsHSTAUBgNVHREE +DTALgglsb2NhbGhvc3QwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAnEl/ +z5IElIjvkK4AgMPrNcRlvIGDt2orEik7b6Jsq6/RiJQ7cSsYTZf7xbqyxNsUOTxv ++frj47MEN448H2nRvUxH29YR3XygV5aEwADSAhwaQWn0QfWTCZbJTmSoNEDtDOzX +TGDlAoCD9s9Xz9S1JpxY4H+WWRZrBSDM6SC1c6CzuEeZRuScNAjYD5mh2v6fOlSy +b8xJWSg0AFlJPCa3ZsA2SKbNqI0uNfJTnkXRm88Z2NHcgtlADbOLKauWfCrpgsCk +cZgo6yAYkOM148h/8wGla1eX+iE1R72NUABGydu8MSQKvc0emWJkGsC1/KqPlf/O +eOUsdwn1yDKHRxDHyA== + + + + + + + + + + diff --git a/x-pack/test/saml_api_integration/fixtures/saml_provider/server/index.ts b/x-pack/test/saml_api_integration/fixtures/saml_provider/server/index.ts new file mode 100644 index 00000000000000..7c3bc5d0321604 --- /dev/null +++ b/x-pack/test/saml_api_integration/fixtures/saml_provider/server/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializer } from '../../../../../../src/core/server'; +import { initRoutes } from './init_routes'; + +export const plugin: PluginInitializer = () => ({ + setup: core => initRoutes(core), + start: () => {}, + stop: () => {}, +}); diff --git a/x-pack/test/saml_api_integration/fixtures/saml_provider/server/init_routes.ts b/x-pack/test/saml_api_integration/fixtures/saml_provider/server/init_routes.ts new file mode 100644 index 00000000000000..5777aa3f423f0c --- /dev/null +++ b/x-pack/test/saml_api_integration/fixtures/saml_provider/server/init_routes.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup } from '../../../../../../src/core/server'; +import { getSAMLResponse, getSAMLRequestId } from '../../saml_tools'; + +export function initRoutes(core: CoreSetup) { + const serverInfo = core.http.getServerInfo(); + core.http.resources.register( + { + path: '/saml_provider/login', + validate: false, + options: { authRequired: false }, + }, + async (context, request, response) => { + const samlResponse = await getSAMLResponse({ + inResponseTo: await getSAMLRequestId(request.url.href!), + destination: `${serverInfo.protocol}://${serverInfo.host}:${serverInfo.port}/api/security/saml/callback`, + }); + + return response.renderHtml({ + body: ` + + Kibana SAML Login + + + +
+ +
+ + `, + }); + } + ); + + core.http.resources.register( + { path: '/saml_provider/login/submit.js', validate: false, options: { authRequired: false } }, + (context, request, response) => { + return response.renderJs({ body: 'document.getElementById("loginForm").submit();' }); + } + ); +} diff --git a/x-pack/test/saved_object_api_integration/common/config.ts b/x-pack/test/saved_object_api_integration/common/config.ts index c6a8599b1797f9..f933cb30ffd043 100644 --- a/x-pack/test/saved_object_api_integration/common/config.ts +++ b/x-pack/test/saved_object_api_integration/common/config.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore import { resolveKibanaPath } from '@kbn/plugin-helpers'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import path from 'path'; diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 66bf3cf7b683ba..275d80786e43c2 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore import { resolveKibanaPath } from '@kbn/plugin-helpers'; import path from 'path'; import { TestInvoker } from './lib/types'; diff --git a/yarn.lock b/yarn.lock index 044d259d5804ec..a18f89cd480f83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1427,10 +1427,10 @@ "@types/node-jose" "1.1.0" node-jose "1.1.0" -"@elastic/static-fs@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@elastic/static-fs/-/static-fs-1.0.1.tgz#2e084e9fc321dd4c7fb4579021ca8a6b26f3464e" - integrity sha512-Vl40Va/h0P6aDUrzgDeTabGVUb/s/W92le64E1UXTcG5927cZtTnOu0datMjr98xdr9C6RAJ3mr6zgxfox5TNw== +"@elastic/static-fs@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@elastic/static-fs/-/static-fs-1.0.2.tgz#c1e5fea6a1b35abcd005cecf7880156ed0f273ae" + integrity sha512-0cZc5D9Wg6pJsc8Sa2ns1eOuxtXEidE7GBb2B0KZdJq9nZzUCxMyplURqT0Nr3i5XpoHb6ZEmxWsji86j1KjDw== "@elastic/ui-ace@0.2.3": version "0.2.3" @@ -3980,6 +3980,11 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== +"@types/expect@^1.20.4": + version "1.20.4" + resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" + integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== + "@types/fancy-log@^1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.1.tgz#dd94fbc8c2e2ab8ab402ca8d04bb8c34965f0696" @@ -4075,6 +4080,20 @@ resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.13.4.tgz#55ae9c29f0fd6b85ee536f5c72b4769d5c5e06b1" integrity sha512-B4yel4ro2nTb3v0pYO8vO6SjgvFJSrwUY+IO6TUSLdOSB+gQFslylrhRCHxvXMIhxB71mv5PEE9dAX+24S8sew== +"@types/gulp-rename@^0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/gulp-rename/-/gulp-rename-0.0.33.tgz#38d146e97786569f74f5391a1b1f9b5198674b6c" + integrity sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA== + dependencies: + "@types/node" "*" + +"@types/gulp-zip@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/gulp-zip/-/gulp-zip-4.0.1.tgz#96cd0b994219f9ae3bbbec7ec3baa043fba9d9ef" + integrity sha512-dYwGsHmwv4pnMD+jtyuIdZchJ0CIivnl8PIApHC+rYN7FMj01tJSAiQb+YN4T/pOn10pmmucBLEB9wXEhQX2Ug== + dependencies: + "@types/node" "*" + "@types/gulp@^4.0.6": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-4.0.6.tgz#68fe0e1f0ff3657cfca46fb564806b744a1bf899" @@ -4172,6 +4191,14 @@ dependencies: "@types/hapi" "*" +"@types/inquirer@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be" + integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw== + dependencies: + "@types/through" "*" + rxjs "^6.4.0" + "@types/intl-relativeformat@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#3a2b0043380388f39c666665ec517e11412f1358" @@ -4478,6 +4505,13 @@ dependencies: "@types/node" "*" +"@types/node-sass@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@types/node-sass/-/node-sass-4.11.0.tgz#b0372075546e83f39df52bd37359eab00165a04d" + integrity sha512-uNpVWhwVmbB5luE7b8vxcJwu5np75YkVTBJS0O3ar+hrxqLfyhOKXg9NYBwJ6mMQX/V6/8d6mMZTB7x2r5x9Bw== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@8.10.54", "@types/node@>=10.17.17 <10.20.0", "@types/node@>=8.9.0", "@types/node@^12.0.2": version "10.17.17" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.17.tgz#7a183163a9e6ff720d86502db23ba4aade5999b8" @@ -4983,6 +5017,28 @@ "@types/react-dom" "*" "@types/testing-library__dom" "*" +"@types/through2-map@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/through2-map/-/through2-map-3.0.0.tgz#2fda6049bf0ec16fd75394fc64536d73024d3189" + integrity sha512-r2m4v3Lggg30dCt7nG9uDl93LhImYRsAutECYNU7JenHTM3MdwMHudcC3sOqk/rEUEpN9CDNOIuOxRGzJUP1pg== + dependencies: + "@types/node" "*" + "@types/through2" "*" + +"@types/through2@*", "@types/through2@^2.0.35": + version "2.0.35" + resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.35.tgz#9add1643da9f936ecf0622311759b33e881047e8" + integrity sha512-5puhsegK8DdiZkVL71+iL67KxKd92l7kzzzeclc+idlp5L6PbjxDDQX9JCIA6jOUS9aNHgcmONyW5CRtZUvKFw== + dependencies: + "@types/node" "*" + +"@types/through@*": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/tinycolor2@^1.4.0": version "1.4.2" resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf" @@ -5079,6 +5135,14 @@ dependencies: "@types/node" "*" +"@types/vinyl@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.4.tgz#9a7a8071c8d14d3a95d41ebe7135babe4ad5995a" + integrity sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ== + dependencies: + "@types/expect" "^1.20.4" + "@types/node" "*" + "@types/watchpack@^1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@types/watchpack/-/watchpack-1.1.5.tgz#e5622eb2a49e2239d94d8882275fbc7893147e97" @@ -31231,7 +31295,7 @@ vinyl@^1.1.0: clone-stats "^0.0.1" replace-ext "0.0.1" -vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0: +vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0, vinyl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" integrity sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==