diff --git a/.eslintrc.js b/.eslintrc.js index 529bc68537aa1..6b3378d44cb81 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -493,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 19116977756f4..85f490bc366b9 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 958ced8c6195a..f435b18c6d824 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 0000000000000..b239319c427fe --- /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 870fa33dce900..4f981b5a40139 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 0000000000000..c8d13bab92b05 --- /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 0000000000000..d8493148bae10 --- /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 b43c484f8de7f..fd05ae139ba21 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 0000000000000..1114e05589c4b --- /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 8140b3fcf380f..0000000000000 --- 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 af770ed3055aa..0000000000000 --- 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 69bc4e473590c..af2a41b691727 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 eafc81447ee03..b2524ec48c757 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 de9e99117fc99..776a76b24c4ec 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 9aa10289d299b..bb619ac79e143 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 96187d847c1c4..0000000000000 --- 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 ea5a1b1848613..0000000000000 --- 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 d29ad55b32db2..0000000000000 --- 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 055ee6d731512..0000000000000 --- 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 771c5c9be9c06..0000000000000 --- 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 f3c71c5d181f8..0000000000000 --- 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 b309494a36662..0000000000000 --- 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 fbede6b4dbad7..0000000000000 --- 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 1758471ced483..0000000000000 --- 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 d3e8bd98aebf4..0000000000000 --- 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 f472ba1d08506..0000000000000 --- 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 91034fea5156a..72a63ec34d2c7 100644 --- a/package.json +++ b/package.json @@ -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 577be820ccc7b..e16ba0d16bb87 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 bed10e77597b7..40c53c7f28f6f 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 18a08332605ea..175ff1019fa2d 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 4c87399319b3b..0000000000000 --- 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 53c998ddfaac2..040b779a69951 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 48b70535272fe..ee1bca0fe3ac2 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 40ac6755f02e5..501c0bd061913 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 497dee8edf740..ce585dd0a02c8 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 5b8be836998e4..1566932b65872 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 f1b288c68c390..68c095209e817 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 8b855a8b8769d..77fa7f2fcae84 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 0000000000000..acc7134308bec --- /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 0000000000000..d66e18f5239a9 --- /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 60baa7fc97660..dc3ef936e2164 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 119b64f71e2f9..2b1a2a63c1074 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 afc9c056d51d7..7817838760a2e 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 6e3d8969802a4..0348f9f8deda9 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 0000000000000..a3f5d144602ed --- /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 32513958f66ab..16207f323ff71 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 0000000000000..a469e4fe67390 --- /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 6df55fda902b0..9fa2305a94eab 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 655b5c95a0102..e3a68f82e48b2 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 b76e91fd5f289..d22d544a95491 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 90deb0c4ac4a8..f596576fe7466 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 42ad1c5ae0cfc..8a4a5a55ce237 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 9659cfbaa2a30..13de8fa165c29 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 64656baee6fd2..255b2e6ef9992 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 b01a64c7f9be2..cf34bdbadf416 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 08ef0914311f8..75affb6da8c6f 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 0cdc615ee867f..be8db50825fc9 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 f16e391b2b211..d07c19291d2cb 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 0472207948c3d..3089357b49991 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 e1ba8caee7f9a..2fe8134209894 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 0000000000000..74e53062d4f01 --- /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 7cc9864baa975..a79ef0a59d19a 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 7d42dc13ef278..0000000000000 --- 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 1ab022db588f9..0000000000000 --- 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 f5559aa7290c5..d0dbe1e44f0fa 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 57cdc8ffd494f..585ce8181df5f 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 c4efa86366bf4..f5bb39a16fe46 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 a42719417a2b1..2979dd7661e59 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 e58114b69dcc1..46e1ecb83e9e4 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 239249f40fe66..9edcf2e3c7990 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 283bfa9e2771a..4c5d6bcde8b77 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 9b672d40961d8..aeeb8c3342e46 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 fe49d4a7c6a58..fbfa9db5635dd 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 3b2d9ed3c0b02..3698fdcfe9512 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 19cfadf70be1b..c4b3c929415ee 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 6d71844bc19c8..6c5ab5fcedcfd 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 225ef611c0298..fbd8f474151fa 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 4ce422e1f65c4..0ca87eae6e235 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 7ac707b0f3d83..0976bfd56682e 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 73f44f3c5ab5c..7c72e3270743e 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 49c4d690c6876..0e639aa72a825 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 4e1f5981d6a41..6f5ecb1eb464b 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 be22652ab2c1c..6b1b7f0d249ff 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 247af7e20d581..c727dcd3e3c65 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 a8376c0e84bf9..24bc4ba8fba5b 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 1a040e236351a..275f1cb687e22 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 fb54c36df27d7..ee729d2b427ad 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 895dc63939311..3b692ba58cbe1 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 f3beb05e577c1..8ef267de2890c 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 fe7f239fbea3b..f9a2234d6e5a4 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 04eeff1e1f3ce..2784b74ab726c 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 0000000000000..1ebf3bcb8f4b6 --- /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 43c41fe64306a..9f4433e7099d8 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 3e9012cd71dc5..e3f99f2407f75 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 77f1faf468b46..c3b9b0909051c 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 1c6ed690df632..1469d357e7e79 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 0000000000000..75a57ab6b405c --- /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 0000000000000..6bf45fb804469 --- /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 46df7036c09cd..8e29d2734065e 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 1a756b205691c..4e3cc15d93ece 100644 --- a/src/plugins/dashboard/public/application/legacy_app.js +++ b/src/plugins/dashboard/public/application/legacy_app.js @@ -49,6 +49,7 @@ export function initDashboardApp(app, deps) { ['listingLimit', { watchDepth: 'reference' }], ['hideWriteControls', { watchDepth: 'reference' }], ['initialFilter', { watchDepth: 'reference' }], + ['initialPageSize', { watchDepth: 'reference' }], ]); }); @@ -108,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); }; diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.js b/src/plugins/dashboard/public/application/listing/dashboard_listing.js index c24e7e4617806..c8cb551fbe561 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 f57c10d1a48dd..ba05c138a0cba 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 e093342f95735..a5211fb3b5d7b 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 07c9feefa0c02..b4419adfe58da 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 00786a0c72cf1..66a96e3e6e129 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/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts index f3297f21c572a..ecbfceac14b66 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 590fee20db690..0bdf8ab7c66f8 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 efb8759e7bead..83a5358642ce4 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 0000000000000..5af62be295201 --- /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 0000000000000..72030d91220b5 --- /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 6889eceb9ac17..f395f28a428fc 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 c4a42642f7fd5..e79689d3e26e3 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 69dec63ae57ba..e2d434074afdb 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 5e7ad6a1d1ea0..60dfb69e85e74 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 5fa37a5ac28eb..fbbbabdbe7bc2 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 8af7380afcdc9..70f277543a52a 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 56597dd31e572..0d9aebe11ece6 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 6b2d20ae8c910..d585c5d6f2638 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 f1bda5fcae10b..ed70c90eb64e6 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 15a948c56148e..20ccc8b182078 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 04502f5fc14e6..a7445a5189163 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 0000000000000..3eca11cc584a9 --- /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/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 4c2dac4f39134..58003a7e9e841 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 c68175ee0a678..e1d047425ac18 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 483605341ae4c..ace8f87bec766 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 0000000000000..77e494626a00e --- /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 18569ef285ff3..1507d6f43619d 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 8fc207e32e6ce..85d27a526d402 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 82789d3c3f55f..3e32ff4fe26b2 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 a8bdd5cca24a3..ecd727e8703ff 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 0000000000000..ef376e80cab07 --- /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 0792955b7c5f1..7ae1b84eecad8 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 81600e9f68634..5b5242579c718 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 e38a0ef9830ea..4f7a4ff7f196f 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 7927238e12066..d430c8896484d 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 0000000000000..4e72bfef0637e --- /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 6f1eb73505b05..9a656fcb510ac 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 0000000000000..2e2e00b2c3ca7 --- /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 e7d6754ac4d05..7c447aefbc69a 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 5d87c11a87198..d66d0b0a28844 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 1b69eb4240d68..afed6b492dc91 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 28eac96dcbf46..b0c6b1952a2a5 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 6a80cdd98b1a3..082f68809a67e 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 0000000000000..fc402d6ab7db5 --- /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 bd6c6d9553930..e33d0e254f609 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 05257cb79a75c..d863937a4e3dc 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 0000000000000..65bc166a47104 --- /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 0bc672bed67c6..e497253761a71 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 b36ba336dbfe5..09256232860bc 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 ca4f36f0ab0f7..f258cb55ba281 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 355c01d255ce7..c201dd3ddc49a 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 836d9de7849dc..7ff81e070f7a5 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 0000000000000..a48cbbae3d0ca --- /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/visualize/public/application/editor/lib/index.ts b/src/plugins/visualize/public/application/editor/lib/index.ts index d2fe028b371c2..78589383925fb 100644 --- a/src/plugins/visualize/public/application/editor/lib/index.ts +++ b/src/plugins/visualize/public/application/editor/lib/index.ts @@ -19,4 +19,4 @@ export { useVisualizeAppState } from './visualize_app_state'; export { makeStateful } from './make_stateful'; -export { addEmbeddableToDashboardUrl } from '../../../../../dashboard/public/url_utils/url_helper'; +export { addEmbeddableToDashboardUrl } from '../../../../../dashboard/public/'; diff --git a/src/plugins/visualize/public/application/listing/visualize_listing.html b/src/plugins/visualize/public/application/listing/visualize_listing.html index 5cae187a393b2..8838348e0b679 100644 --- a/src/plugins/visualize/public/application/listing/visualize_listing.html +++ b/src/plugins/visualize/public/application/listing/visualize_listing.html @@ -8,5 +8,6 @@ get-view-element="listingController.getViewElement" edit-item="listingController.editItem" listing-limit="listingController.listingLimit" + initial-page-size="listingController.initialPageSize" >
diff --git a/src/plugins/visualize/public/application/listing/visualize_listing.js b/src/plugins/visualize/public/application/listing/visualize_listing.js index 2a8c80f9352a5..7c95fd6a7f4b0 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 3d49194c6778f..a1cc11c93cafc 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 7642c6575f211..ace9e50d400c5 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 45316fd6fad62..9d1e89a024b61 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 7f01c64d291a5..e72ded3a6f2fd 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 c5891c704dd6a..096e237850c72 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 6af91ac9c5c94..1a85ff86498dc 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 f085bad4c507e..cbaa0306f9a42 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/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 13d2365c07191..4b5a3c101b51c 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 cd12f1b75c828..aa67c455e0100 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 3697e94461074..727d81377b141 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 0000000000000..fc483ca512679 --- /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 4c5728174cf99..daa55240f3eb7 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 84526878a7bb4..2e0b360e594e5 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 090dc995ddc11..e4e0e7ce70bc4 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 02ed9e9865d9a..cbb0c6790dbe9 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 f3b7a19f70ae3..96297f6d51566 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 0c185f4b385b5..6ad2bb56391dd 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 07c829e8b15a0..2aa81f34028a0 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 cb20b434ba34c..166a86c169261 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 6252a103d2881..f4c44a8ae2df4 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 8ae37d1c44637..5092f631e7b39 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 7c464d44d5761..7ac27dd47ad64 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 31fa84906cd38..8d22b36e96d45 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 3355bc9881811..b5399595fb782 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 b2cfd826e6207..0000000000000 --- 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 b8ecb644ff1b0..0000000000000 --- 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 e2e31a218943a..fe8919f48ac10 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 273584efb239b..4cb38bb3d057b 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 62759ce0f5215..b5056140c8b86 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 90ba4056d7c98..db19e5b6a7627 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 e1532cac858ac..89ce976de9278 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 70c1c4a50efd4..721b30a208ebc 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 e2a758075155a..b7cd1687dc257 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 c152f5bfffc31..46949a2e3e2cf 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 6ce457ffbec05..1f8a4a62ea156 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 871d86e642dec..0fe056ec0d46f 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 75c6c79bc804a..9001eb6992a96 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 3bbd8a01d0549..c0c93bb4cb298 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 91483b4b52d90..e1f58b7b35210 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 5d1ca923cbc8f..67c6cd9978bf1 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 b5bfa63c1bdde..e58d9a0ff761a 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 1c9d5092bfcf5..86ef0b880c4da 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/ServiceOverview/NoServicesMessage.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx index c1afa433cb614..266e5a97ef07a 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 227becb9a9c4f..d027422961c99 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 6d310199ba9a5..3e6be107ce3a1 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 99b169e3ec361..06f56d9ec1be7 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 d2afefb83a568..96e8c754fcc5f 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 36e780f50c3ae..8ed02f039289e 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 1a3394651b2ff..aa23a8a2e64ea 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 0000000000000..92ca870810ce8 --- /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 0000000000000..1b431216ef992 --- /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 cebefabe77cdc..6711e93895b62 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 0000000000000..9a45be702e212 --- /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 70b7bd3df0662..107cb987f2107 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 690d471b306ab..38f1f5b025532 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 3ffd42c5201f9..9116f7a37ce92 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 ee97198211b56..f7a8ea413531f 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 efe5eb25a0ac1..13a0003b5ebdf 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 f15e08c2ca230..1dcceac5c710d 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 cd75f882186c2..19acb1441df4c 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 534da6541b683..ae158e9d03476 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 6fff7574c39e6..65e0d19aa4770 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 11336f230c840..0469a62319813 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 20eb2b9c3fd0d..4390e73b6fc06 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 7fa0231cf3409..5880871da9f13 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 4912abec90682..5ea4b643fb5a2 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 7340f6b56d6f6..1c2a337798b25 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 08af22f7cf4cc..345d264878503 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 66d52b8dcc5dc..007f1f9535bc4 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 0000000000000..9a3d1a9dc0f53 --- /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 0000000000000..ca5c63802aac5 --- /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 6776fb0b6f5ad..6851020cdf97e 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 815b80e55fa11..1366894f78ddb 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 319e37e226c9e..74fd07d5d3dff 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 b2e11461007fd..880af83bfb3f2 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 e8e2d5cd6b430..413cf32529b2f 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 b1405cfbf2e60..ab36c7fbbae0e 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 be501a5e951ba..640da59ea572d 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 6e4665fb130de..16339c8eac003 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 fc4daf3df60b2..bda157cd7f12e 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 06e6fac0d75c4..4473e015d9d74 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 96b64e8171af5..9d7a1954b60d9 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 0000000000000..1ae21a561950d --- /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 9045c7ded2ada..52d185e0d7dc9 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 5030b1704b1d7..b0ddf0309232a 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 e26c6c9eb8099..357790c34ee58 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 190c9e265463d..4f6852cb6a007 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 92cfcc935ad9b..7bdea96db0f2f 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 5432a39953b84..a6cfe0b88dcf8 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 2ebda89ba13fd..d750417640f8c 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 83129384a77df..093d618ba8d8b 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 c3b21b5df5175..ffb431da1f43e 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 82576bff2cbfd..a9ca69c44c97d 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 141aa7168083d..9e4271c683415 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 86562be3ff989..2d67edf7e347e 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 3952b44f82561..6fae3a5efc71f 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 80590febc95be..bea84377d2ab8 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 672c0d89bb002..5a65473b25a24 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 635b9f03e8b38..5151f501ddba1 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 57b007753491c..4df1a7d065469 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 326c529af3c61..65fe8bf5d52c1 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 553128df76baa..918ae0bb6fcb2 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 93489f5223ef8..71f0b8161131f 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 8dc3afeb06c6b..525873e1dd64b 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 0000000000000..607fb0ab2725d --- /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/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index 586c2b0c2a259..283196373fe9f 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/cross_cluster_replication/common/constants/index.ts b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts index 797141b0996af..96884cf4bead8 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 dfe9e4e657c30..561da838a4202 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 25c99803480f3..7ef085a21ac1a 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 { 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 6e51f3b650710..1cab1821b1bf5 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 ebaa5f1a504ca..d9437169a7453 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 0000000000000..3d7cf19e58b0e --- /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 2405f05768a2f..e3480eda9fe7d 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 c43e2a5195fe7..67b28b27f931f 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 428500c3daa88..d07b488693091 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 3c3ee2b125896..42c05cfde1a44 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 96e7ea3ff2232..4cae14f8939b2 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 2f75d663126b2..4e0c13442d267 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 7effe44375b1f..d4199fbd092b4 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 4aff9d28f3771..7c0fb867b9ada 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 37f27523cbad2..b89ee2489d7f3 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 b869119062538..9d6d083b3e4b9 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 dbdbe2b52bd56..d64c8c6239fcd 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 cbc735bb150f5..857a63826505e 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 3a49e2870b609..c17d96250b1fb 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/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts index e56e8d474d780..8bd9fc2432232 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/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 78e80687abeb4..5fb8ce7207729 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 cd2964df23d9b..8942367261511 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/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx index 8bcf0e9ed5be5..bafb38459b17b 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 d2904206875c7..a3cebcf33f386 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 dd888639b6d07..d808b4f3b64aa 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/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts index 64669b5cd123a..085bad2d18375 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 edf681c276a84..de291e364e02f 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 0ab46f386e83b..d537f8d68d7e4 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 cfa946ff942ec..a2ee321526052 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/license_management/__jest__/__snapshots__/add_license.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap index 03421e66c77f5..e4411807dfa56 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 9ac8b14236685..b621e89efbee3 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 f68e1dcfaf62b..9f0019680d14b 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 dab4c4048ce4c..31910d81b3434 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 dbe31fed66413..96d28bf618ce4 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 edc037ed7a0b1..cd69685ffcb1b 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 8f23f3744e563..d008a82308153 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 6a60a6df55691..774378c6318f5 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 0000000000000..f899fd69110fa --- /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 0000000000000..d38d3cc038525 --- /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 0000000000000..f19c50c6b42ae --- /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 0000000000000..5ba3bf4e8f43b --- /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 6cba81e47fbcc..6d16f2074864c 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 7a6e2a707873c..68df80b2a42dd 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 0000000000000..607e05ef8286f --- /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 0000000000000..7a6086514f943 --- /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 96f054b304962..91887395e747d 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 0000000000000..3fc51dd20b0b3 --- /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 0000000000000..f795be9493fbf --- /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 c1745dda7afab..73d9a53a41e4f 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 d332ab1eb1bab..0dbd9297b773e 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 536931f715f3f..2016069f32fb3 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 59d1a66a581a0..653a42dc91653 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 0000000000000..095fcd2f63b48 --- /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 0000000000000..5593e640f71ac --- /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 b69523b664fd7..394c1f1e2289a 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 0000000000000..162406a6d6589 --- /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 0000000000000..e8a0dcd4994a2 --- /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 23701ff753bc0..3a42cf28665f5 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 8223a6a34b771..4c5c8c429a14c 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 0000000000000..15e1c92c06d13 --- /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 0000000000000..1940d94597dec --- /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 0000000000000..a58bf433017e6 --- /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 0000000000000..a2ea09a3263ae --- /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 3f11adf58d8d4..213685d1183bd 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 0000000000000..a08bab65c881c --- /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 0000000000000..8d42be2d145ca --- /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 0000000000000..43698665bb371 --- /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 0000000000000..c6ebffa379903 --- /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 0000000000000..4fb4133d7353f --- /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 0000000000000..750e04e2f39e5 --- /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 0000000000000..674d7c40c2970 --- /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 5e74753a6f0bd..eb4b393a36da3 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 5facf981c098e..ed515757875be 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 0000000000000..ddcae137a961a --- /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 0000000000000..c8a1b080c16f6 --- /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 0000000000000..e10ffab5359b0 --- /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 0000000000000..ef30ab6ab64c5 --- /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 51b4eb9f02cc2..82dfe8a4f29d0 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 0000000000000..3b5503ffb9833 --- /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 0000000000000..41c0c0760e03b --- /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 4951cddc56939..97f497bca7183 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 924dd086ee708..16f96d99505d8 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 0000000000000..77d37373549c7 --- /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 0000000000000..1668124acdfce --- /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 0a60cba786f04..10c7f781f554c 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 0000000000000..478225ee35eb8 --- /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 0000000000000..a112c7422b952 --- /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 0000000000000..665a7540184a0 --- /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 0000000000000..8e6730ef3db5c --- /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 0000000000000..ba01ca617fb8b --- /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 a16163ec0fa3a..6ad69fd994bfd 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 a601bdfc003c5..90214872b68e3 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 0000000000000..9e1a708e0c83b --- /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 0000000000000..b5077ae100cde --- /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 0000000000000..aab5ffd0e57fe --- /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 0000000000000..bb431800c56c3 --- /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 0000000000000..fe2ca501b4416 --- /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 0000000000000..a87881b385328 --- /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 0000000000000..7e09452a23e11 --- /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 0000000000000..bbfbc3135ddb8 --- /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 9934ce61c7107..ce9fdd6aa21d4 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 0000000000000..520bc4ddf1e09 --- /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 0000000000000..8c039bf6788ae --- /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 0000000000000..2b97f37a7fa6b --- /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 0000000000000..d68a26eb8ffe2 --- /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 0000000000000..a7fbe1ea48c02 --- /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 0000000000000..a53079318edfa --- /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 0000000000000..85c5b0e518fab --- /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 0000000000000..a1ee184b3e5bb --- /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 0000000000000..34e6de2576879 --- /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 0000000000000..0420a1f702328 --- /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 0000000000000..ac8337aab8368 --- /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 0000000000000..575a529c69906 --- /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 861928866369b..e00c3a9ace34f 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 0000000000000..84a775ffcf7f1 --- /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 0000000000000..6cee54b1a6148 --- /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 0000000000000..d7523a0804a89 --- /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 0000000000000..029bfcdabee3e --- /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 0000000000000..7ba832e72bb8e --- /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 0000000000000..4a6dc1da97854 --- /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 0000000000000..6904438c8d275 --- /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 0000000000000..3b2d991281cd6 --- /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 0000000000000..8031f085c1635 --- /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 0000000000000..6e71ed1b3e59d --- /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 0000000000000..cecd6bf3397a7 --- /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 0000000000000..539dda673208b --- /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 0000000000000..d635cafbd3b1b --- /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 0000000000000..8b28443b4e30c --- /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 0000000000000..7ef3e4af3d604 --- /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 0000000000000..a66f00819605b --- /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 0000000000000..dbb188bc2754a --- /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 0000000000000..6c5ccb5e1f2fd --- /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 0000000000000..4e955d4281c4d --- /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 0000000000000..505ebc43afc5a --- /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 ba22bf72cc18c..cba48115c746c 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 8a44b5ab607bf..e6365e689f761 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 e78debc8e4349..81ebe9f17b06f 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 d7c3208e556fa..6808aaa04ab7a 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 322b9860b3785..4ef259b84e24f 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 7fbed5b3b8602..7d4afc0a4ea13 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 80e417f9dfa62..4bdafcabaad06 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 5aa7c74a7b30b..1dfdfc3a73d8a 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 539d0ab4d6ade..04daedc59c032 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 69f5033e3af0f..5108e5cd3e6a3 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 bb4fbe9d01b60..f8f78f6e97829 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 6c04f7c19ac7d..d32593f73c46c 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 fe1ff58922162..4ffd0d93fd22a 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 0000000000000..057907353d68a --- /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 0000000000000..1c478bb85ccc7 --- /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 b19c25b369848..f4c2b8d926075 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 05e2ad06842ce..27c4fca7d701d 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 0000000000000..c271943213027 --- /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 451a79dd3864a..56a461a3bb147 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 0000000000000..ad8b8d37f1310 --- /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 0000000000000..4247233b295e1 --- /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/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 01cce153ce494..68e728c019873 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 19b51f7615345..236a8083a95e6 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 2d6505f5ce1f7..126fd25a536f6 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 2a65ee06f2c2c..307fa79f5dea2 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 7036b4f64b3c5..9874ac56577d3 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 0000000000000..5f72d155cbd5a --- /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 1f2a57f999775..4f77004b91f99 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 6bc5c9b15074f..f15cdb12afb21 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 3e4e9cfbd2b66..87a7156b6f52e 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 0000000000000..a6ca575f21b50 --- /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/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index feda891c1ce29..69d7727f9a20a 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 085cc9082cf53..e8ea1f8df227a 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 5fcb6b50f5d83..b8c854f4e7ee0 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 23fe6758542c9..770447ce33f93 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 22f98e94748d8..fde8ffa511319 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 34f0bc9ac8a36..7ab9a15e1773a 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 66366cc0b520d..f600b1ebbb96c 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 83a7ca6bc5967..44829f38e79cd 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 0e0333cf30f17..b2e793d7e75e9 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 ee9a1844c7468..575bc9bf9dff1 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/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts index 53c12ad7ab12c..466e1aa9b3c68 100644 --- a/x-pack/plugins/security/public/management/management_service.test.ts +++ b/x-pack/plugins/security/public/management/management_service.test.ts @@ -27,9 +27,8 @@ describe('ManagementService', () => { const mockSection = { registerApp: jest.fn() }; const managementSetup = { sections: { - getSection: jest.fn(), + getSection: jest.fn().mockReturnValue(mockSection), getAllSections: jest.fn(), - register: jest.fn().mockReturnValue(mockSection), }, }; @@ -42,14 +41,6 @@ describe('ManagementService', () => { management: managementSetup, }); - expect(managementSetup.sections.register).toHaveBeenCalledTimes(1); - expect(managementSetup.sections.register).toHaveBeenCalledWith({ - id: 'security', - title: 'Security', - order: 100, - euiIconType: 'securityApp', - }); - expect(mockSection.registerApp).toHaveBeenCalledTimes(4); expect(mockSection.registerApp).toHaveBeenCalledWith({ id: 'users', @@ -96,9 +87,8 @@ describe('ManagementService', () => { authc: securityMock.createSetup().authc, management: { sections: { - getSection: jest.fn(), + getSection: jest.fn().mockReturnValue({ registerApp: jest.fn() }), getAllSections: jest.fn(), - register: jest.fn().mockReturnValue({ registerApp: jest.fn() }), }, }, }); diff --git a/x-pack/plugins/security/public/management/management_service.ts b/x-pack/plugins/security/public/management/management_service.ts index 7c4c470730ffe..2dc3ecbd9f3a2 100644 --- a/x-pack/plugins/security/public/management/management_service.ts +++ b/x-pack/plugins/security/public/management/management_service.ts @@ -5,12 +5,12 @@ */ import { Subscription } from 'rxjs'; -import { i18n } from '@kbn/i18n'; import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public'; import { ManagementApp, ManagementSetup, ManagementStart, + ManagementSectionId, } from '../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../common/licensing'; import { AuthenticationServiceSetup } from '../authentication'; @@ -39,14 +39,7 @@ export class ManagementService { setup({ getStartServices, management, authc, license, fatalErrors }: SetupParams) { this.license = license; - const securitySection = management.sections.register({ - id: 'security', - title: i18n.translate('xpack.security.management.securityTitle', { - defaultMessage: 'Security', - }), - order: 100, - euiIconType: 'securityApp', - }); + const securitySection = management.sections.getSection(ManagementSectionId.Security); securitySection.registerApp(usersManagementApp.create({ authc, getStartServices })); securitySection.registerApp( @@ -58,7 +51,7 @@ export class ManagementService { start({ management }: StartParams) { this.licenseFeaturesSubscription = this.license.features$.subscribe(async features => { - const securitySection = management.sections.getSection('security')!; + const securitySection = management.sections.getSection(ManagementSectionId.Security); const securityManagementAppsStatuses: Array<[ManagementApp, boolean]> = [ [securitySection.getApp(usersManagementApp.id)!, features.showLinks], diff --git a/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts b/x-pack/plugins/siem/common/exact_check.test.ts similarity index 98% rename from x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts rename to x-pack/plugins/siem/common/exact_check.test.ts index 1e70deaeed438..a5701603de0d6 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts +++ b/x-pack/plugins/siem/common/exact_check.test.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; import { left, right, Either } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; import { exactCheck, findDifferencesRecursive } from './exact_check'; +import { foldLeftRight, getPaths } from './test_utils'; describe('exact_check', () => { test('it returns an error if given extra object properties', () => { diff --git a/x-pack/plugins/siem/server/utils/build_validation/exact_check.ts b/x-pack/plugins/siem/common/exact_check.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/exact_check.ts rename to x-pack/plugins/siem/common/exact_check.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts b/x-pack/plugins/siem/common/format_errors.test.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts rename to x-pack/plugins/siem/common/format_errors.test.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/format_errors.ts b/x-pack/plugins/siem/common/format_errors.ts similarity index 100% rename from x-pack/plugins/siem/server/utils/build_validation/format_errors.ts rename to x-pack/plugins/siem/common/format_errors.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts b/x-pack/plugins/siem/common/test_utils.ts similarity index 95% rename from x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts rename to x-pack/plugins/siem/common/test_utils.ts index 578972dda5aef..29b0119dcbeb7 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts +++ b/x-pack/plugins/siem/common/test_utils.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { formatErrors } from '../format_errors'; +import { formatErrors } from './format_errors'; interface Message { errors: t.Errors; diff --git a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.test.tsx b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.test.tsx index 579f8869c08b1..a9bde76126b6e 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.test.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.test.tsx @@ -13,7 +13,7 @@ import { useFormFieldMock } from '../../../../common/mock'; jest.mock('../../../../common/lib/kibana'); describe('RuleActionsField', () => { - it('should not render ActionForm is no actions are supported', () => { + it('should not render ActionForm if no actions are supported', () => { (useKibana as jest.Mock).mockReturnValue({ services: { triggers_actions_ui: { diff --git a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx index 2e9a793bbdef2..e503a090fe762 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/index.tsx @@ -4,16 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useEffect, useState } from 'react'; +import { isEmpty } from 'lodash/fp'; +import { EuiSpacer, EuiCallOut } from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import deepMerge from 'deepmerge'; +import ReactMarkdown from 'react-markdown'; +import styled from 'styled-components'; import { NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS } from '../../../../../common/constants'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { loadActionTypes } from '../../../../../../triggers_actions_ui/public/application/lib/action_connector_api'; import { SelectField } from '../../../../shared_imports'; -import { ActionForm, ActionType } from '../../../../../../triggers_actions_ui/public'; +import { + ActionForm, + ActionType, + loadActionTypes, +} from '../../../../../../triggers_actions_ui/public'; import { AlertAction } from '../../../../../../alerting/common'; import { useKibana } from '../../../../common/lib/kibana'; +import { FORM_ERRORS_TITLE } from './translations'; type ThrottleSelectField = typeof SelectField; @@ -21,7 +28,14 @@ const DEFAULT_ACTION_GROUP_ID = 'default'; const DEFAULT_ACTION_MESSAGE = 'Rule {{context.rule.name}} generated {{state.signals_count}} signals'; +const FieldErrorsContainer = styled.div` + p { + margin-bottom: 0; + } +`; + export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables }) => { + const [fieldErrors, setFieldErrors] = useState(null); const [supportedActionTypes, setSupportedActionTypes] = useState(); const { http, @@ -31,13 +45,18 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables application: { capabilities }, } = useKibana().services; + const actions: AlertAction[] = useMemo( + () => (!isEmpty(field.value) ? (field.value as AlertAction[]) : []), + [field.value] + ); + const setActionIdByIndex = useCallback( (id: string, index: number) => { - const updatedActions = [...(field.value as Array>)]; + const updatedActions = [...(actions as Array>)]; updatedActions[index] = deepMerge(updatedActions[index], { id }); field.setValue(updatedActions); }, - [field] + [field.setValue, actions] ); const setAlertProperty = useCallback( @@ -48,11 +67,11 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables const setActionParamsProperty = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (key: string, value: any, index: number) => { - const updatedActions = [...(field.value as AlertAction[])]; + const updatedActions = [...actions]; updatedActions[index].params[key] = value; field.setValue(updatedActions); }, - [field] + [field.setValue, actions] ); useEffect(() => { @@ -65,23 +84,57 @@ export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables })(); }, []); + useEffect(() => { + if (field.form.isSubmitting || !field.errors.length) { + return setFieldErrors(null); + } + if ( + field.form.isSubmitted && + !field.form.isSubmitting && + field.form.isValid === false && + field.errors.length + ) { + const errorsString = field.errors.map(({ message }) => message).join('\n'); + return setFieldErrors(errorsString); + } + }, [ + field.form.isSubmitted, + field.form.isSubmitting, + field.isChangingValue, + field.form.isValid, + field.errors, + setFieldErrors, + ]); + if (!supportedActionTypes) return <>; return ( - + <> + {fieldErrors ? ( + <> + + + + + + + + ) : null} + + ); }; diff --git a/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/translations.tsx b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/translations.tsx new file mode 100644 index 0000000000000..8bf8e39685dfd --- /dev/null +++ b/x-pack/plugins/siem/public/alerts/components/rules/rule_actions_field/translations.tsx @@ -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 { i18n } from '@kbn/i18n'; + +export const FORM_ERRORS_TITLE = i18n.translate( + 'xpack.siem.detectionEngine.createRule.ruleActionsField.ruleActionsFormErrorsTitle', + { + defaultMessage: 'Please fix issues listed below', + } +); diff --git a/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx b/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx index 58112732bea3b..4a21eb0fbcf23 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/select_rule_type/index.tsx @@ -77,7 +77,7 @@ export const SelectRuleType: React.FC = ({ const setQuery = useCallback(() => setType('query'), [setType]); const mlCardDisabled = isReadOnly || !hasValidLicense || !isMlAdmin; const licensingUrl = useKibana().services.application.getUrlForApp('kibana', { - path: '#/management/elasticsearch/license_management', + path: '#/management/stack/license_management', }); return ( diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.test.tsx b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.test.tsx index 712aacd3e3e82..165aa5a30b0f0 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.test.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.test.tsx @@ -9,7 +9,18 @@ import { shallow } from 'enzyme'; import { StepRuleActions } from './index'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/lib/kibana', () => ({ + useKibana: jest.fn().mockReturnValue({ + services: { + application: { + getUrlForApp: jest.fn(), + }, + triggers_actions_ui: { + actionTypeRegistry: jest.fn(), + }, + }, + }), +})); describe('StepRuleActions', () => { it('renders correctly', () => { diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.tsx index 86d2eb557e074..8f3a13ef94b58 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/index.tsx @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiHorizontalRule, EuiFlexGroup, EuiFlexItem, EuiButton, EuiSpacer } from '@elastic/eui'; +import { + EuiHorizontalRule, + EuiForm, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiSpacer, +} from '@elastic/eui'; +import { findIndex } from 'lodash/fp'; import React, { FC, memo, useCallback, useEffect, useMemo, useState } from 'react'; import deepEqual from 'fast-deep-equal'; @@ -24,7 +32,7 @@ import { } from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; -import { schema } from './schema'; +import { getSchema } from './schema'; import * as I18n from './translations'; interface StepRuleActionsProps extends RuleStepProps { @@ -42,6 +50,15 @@ const stepActionsDefaultValue = { const GhostFormField = () => <>; +const getThrottleOptions = (throttle?: string | null) => { + // Add support for throttle options set by the API + if (throttle && findIndex(['value', throttle], THROTTLE_OPTIONS) < 0) { + return [...THROTTLE_OPTIONS, { value: throttle, text: throttle }]; + } + + return THROTTLE_OPTIONS; +}; + const StepRuleActionsComponent: FC = ({ addPadding = false, defaultValues, @@ -54,8 +71,12 @@ const StepRuleActionsComponent: FC = ({ }) => { const [myStepData, setMyStepData] = useState(stepActionsDefaultValue); const { - services: { application }, + services: { + application, + triggers_actions_ui: { actionTypeRegistry }, + }, } = useKibana(); + const schema = useMemo(() => getSchema({ actionTypeRegistry }), [actionTypeRegistry]); const { form } = useForm({ defaultValue: myStepData, @@ -104,6 +125,12 @@ const StepRuleActionsComponent: FC = ({ setMyStepData, ]); + const throttleOptions = useMemo(() => { + const throttle = myStepData.throttle; + + return getThrottleOptions(throttle); + }, [myStepData]); + const throttleFieldComponentProps = useMemo( () => ({ idAria: 'detectionEngineStepRuleActionsThrottle', @@ -112,7 +139,7 @@ const StepRuleActionsComponent: FC = ({ hasNoInitialSelection: false, handleChange: updateThrottle, euiFieldProps: { - options: THROTTLE_OPTIONS, + options: throttleOptions, }, }), [isLoading, updateThrottle] @@ -126,30 +153,39 @@ const StepRuleActionsComponent: FC = ({ <>
- - {myStepData.throttle !== stepActionsDefaultValue.throttle && ( - <> - + + + {myStepData.throttle !== stepActionsDefaultValue.throttle ? ( + <> + + + + + + ) : ( - - - )} - + )} + +
diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.test.tsx b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.test.tsx new file mode 100644 index 0000000000000..d746d42aefe78 --- /dev/null +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.test.tsx @@ -0,0 +1,166 @@ +/* + * 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 { validateSingleAction, validateRuleActionsField } from './schema'; +import { isUuidv4, getActionTypeName, validateMustache, validateActionParams } from './utils'; +import { actionTypeRegistryMock } from '../../../../../../triggers_actions_ui/public/application/action_type_registry.mock'; +import { FormHook } from '../../../../shared_imports'; +jest.mock('./utils'); + +describe('stepRuleActions schema', () => { + const actionTypeRegistry = actionTypeRegistryMock.create(); + + describe('validateSingleAction', () => { + it('should validate single action', () => { + (isUuidv4 as jest.Mock).mockReturnValue(true); + (validateActionParams as jest.Mock).mockReturnValue([]); + (validateMustache as jest.Mock).mockReturnValue([]); + + expect( + validateSingleAction( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + actionTypeRegistry + ) + ).toHaveLength(0); + }); + + it('should validate single action with invalid mustache template', () => { + (isUuidv4 as jest.Mock).mockReturnValue(true); + (validateActionParams as jest.Mock).mockReturnValue([]); + (validateMustache as jest.Mock).mockReturnValue(['Message is not valid mustache template']); + + const errors = validateSingleAction( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: { + message: '{{{mustache}}', + }, + }, + actionTypeRegistry + ); + + expect(errors).toHaveLength(1); + expect(errors[0]).toEqual('Message is not valid mustache template'); + }); + + it('should validate single action with incorrect id', () => { + (isUuidv4 as jest.Mock).mockReturnValue(false); + (validateMustache as jest.Mock).mockReturnValue([]); + (validateActionParams as jest.Mock).mockReturnValue([]); + + const errors = validateSingleAction( + { + id: '823d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + actionTypeRegistry + ); + expect(errors).toHaveLength(1); + expect(errors[0]).toEqual('No connector selected'); + }); + }); + + describe('validateRuleActionsField', () => { + it('should validate rule actions field', () => { + const validator = validateRuleActionsField(actionTypeRegistry); + + const result = validator({ + path: '', + value: [], + form: {} as FormHook, + formData: jest.fn(), + errors: [], + }); + + expect(result).toEqual(undefined); + }); + + it('should validate incorrect rule actions field', () => { + (getActionTypeName as jest.Mock).mockReturnValue('Slack'); + const validator = validateRuleActionsField(actionTypeRegistry); + + const result = validator({ + path: '', + value: [ + { + id: '3', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + ], + form: {} as FormHook, + formData: jest.fn(), + errors: [], + }); + + expect(result).toEqual({ + code: 'ERR_FIELD_FORMAT', + message: ` +**Slack:** +* No connector selected +`, + path: '', + }); + }); + + it('should validate multiple incorrect rule actions field', () => { + (isUuidv4 as jest.Mock).mockReturnValueOnce(false); + (getActionTypeName as jest.Mock).mockReturnValueOnce('Slack'); + (isUuidv4 as jest.Mock).mockReturnValueOnce(true); + (getActionTypeName as jest.Mock).mockReturnValueOnce('Pagerduty'); + (validateActionParams as jest.Mock).mockReturnValue(['Summary is required']); + (validateMustache as jest.Mock).mockReturnValue(['Component is not valid mustache template']); + const validator = validateRuleActionsField(actionTypeRegistry); + + const result = validator({ + path: '', + value: [ + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + { + id: 'a8d1ef21-dcb9-4ac6-9e52-961f938a4c17', + group: 'default', + actionTypeId: '.pagerduty', + params: { + component: '{{{', + }, + }, + ], + form: {} as FormHook, + formData: jest.fn(), + errors: [], + }); + + expect(result).toEqual({ + code: 'ERR_FIELD_FORMAT', + message: ` +**Slack:** +* No connector selected + + +**Pagerduty:** +* Summary is required +* Component is not valid mustache template +`, + path: '', + }); + }); + }); +}); diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.tsx b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.tsx index b2f8b79e3f62c..189b55ae5622c 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/schema.tsx @@ -8,9 +8,69 @@ import { i18n } from '@kbn/i18n'; -import { FormSchema } from '../../../../shared_imports'; +import { + AlertAction, + ActionTypeRegistryContract, +} from '../../../../../../triggers_actions_ui/public'; +import { FormSchema, FormData, ValidationFunc, ERROR_CODE } from '../../../../shared_imports'; +import * as I18n from './translations'; +import { isUuidv4, getActionTypeName, validateMustache, validateActionParams } from './utils'; -export const schema: FormSchema = { +export const validateSingleAction = ( + actionItem: AlertAction, + actionTypeRegistry: ActionTypeRegistryContract +): string[] => { + if (!isUuidv4(actionItem.id)) { + return [I18n.NO_CONNECTOR_SELECTED]; + } + + const actionParamsErrors = validateActionParams(actionItem, actionTypeRegistry); + const mustacheErrors = validateMustache(actionItem.params); + + return [...actionParamsErrors, ...mustacheErrors]; +}; + +export const validateRuleActionsField = (actionTypeRegistry: ActionTypeRegistryContract) => ( + ...data: Parameters +): ReturnType> | undefined => { + const [{ value, path }] = data as [{ value: AlertAction[]; path: string }]; + + const errors = value.reduce((acc, actionItem) => { + const errorsArray = validateSingleAction(actionItem, actionTypeRegistry); + + if (errorsArray.length) { + const actionTypeName = getActionTypeName(actionItem.actionTypeId); + const errorsListItems = errorsArray.map(error => `* ${error}\n`); + + return [...acc, `\n**${actionTypeName}:**\n${errorsListItems.join('')}`]; + } + + return acc; + }, [] as string[]); + + if (errors.length) { + return { + code: 'ERR_FIELD_FORMAT', + path, + message: `${errors.join('\n')}`, + }; + } +}; + +export const getSchema = ({ + actionTypeRegistry, +}: { + actionTypeRegistry: ActionTypeRegistryContract; +}): FormSchema => ({ + actions: { + validations: [ + { + validator: validateRuleActionsField(actionTypeRegistry), + }, + ], + }, + enabled: {}, + kibanaSiemAppUrl: {}, throttle: { label: i18n.translate( 'xpack.siem.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', @@ -26,14 +86,4 @@ export const schema: FormSchema = { } ), }, - actions: { - label: i18n.translate( - 'xpack.siem.detectionEngine.createRule.stepRuleActions.fieldActionsLabel', - { - defaultMessage: 'Actions', - } - ), - }, - enabled: {}, - kibanaSiemAppUrl: {}, -}; +}); diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/translations.tsx index 67bcc1af8150b..d0c990df81ffe 100644 --- a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/translations.tsx @@ -5,17 +5,36 @@ */ import { i18n } from '@kbn/i18n'; +import { startCase } from 'lodash/fp'; export const COMPLETE_WITHOUT_ACTIVATING = i18n.translate( - 'xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithoutActivatingTitle', + 'xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithoutActivatingTitle', { defaultMessage: 'Create rule without activating it', } ); export const COMPLETE_WITH_ACTIVATING = i18n.translate( - 'xpack.siem.detectionEngine.createRule. stepScheduleRule.completeWithActivatingTitle', + 'xpack.siem.detectionEngine.createRule.stepScheduleRule.completeWithActivatingTitle', { defaultMessage: 'Create & activate rule', } ); + +export const NO_CONNECTOR_SELECTED = i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepRuleActions.noConnectorSelectedErrorMessage', + { + defaultMessage: 'No connector selected', + } +); + +export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) => + i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage', + { + defaultMessage: '{key} is not valid mustache template', + values: { + key: startCase(paramKey), + }, + } + ); diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.test.ts b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.test.ts new file mode 100644 index 0000000000000..74c9c35d72494 --- /dev/null +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { actionTypeRegistryMock } from '../../../../../../triggers_actions_ui/public/application/action_type_registry.mock'; +import { isUuidv4, getActionTypeName, validateMustache, validateActionParams } from './utils'; + +describe('stepRuleActions utils', () => { + describe('isUuidv4', () => { + it('should validate proper uuid v4 value', () => { + expect(isUuidv4('817b8bca-91d1-4729-8ee1-3a83aaafd9d4')).toEqual(true); + }); + + it('should validate incorrect uuid v4 value', () => { + expect(isUuidv4('ad9d4')).toEqual(false); + }); + }); + + describe('getActionTypeName', () => { + it('should return capitalized action type name', () => { + expect(getActionTypeName('.slack')).toEqual('Slack'); + }); + + it('should return empty string actionTypeId had improper format', () => { + expect(getActionTypeName('slack')).toEqual(''); + }); + }); + + describe('validateMustache', () => { + it('should validate mustache template', () => { + expect( + validateMustache({ + message: 'Mustache Template {{variable}}', + }) + ).toHaveLength(0); + }); + + it('should validate incorrect mustache template', () => { + expect( + validateMustache({ + message: 'Mustache Template {{{variable}}', + }) + ).toHaveLength(1); + }); + }); + + describe('validateActionParams', () => { + const validateParamsMock = jest.fn(); + const actionTypeRegistry = actionTypeRegistryMock.create(); + + beforeAll(() => { + const actionMock = { + id: 'id', + iconClass: 'iconClass', + validateParams: validateParamsMock, + selectMessage: 'message', + validateConnector: jest.fn(), + actionConnectorFields: null, + actionParamsFields: null, + }; + actionTypeRegistry.get.mockReturnValue(actionMock); + }); + + it('should validate action params', () => { + validateParamsMock.mockReturnValue({ errors: [] }); + + expect( + validateActionParams( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: { + message: 'Message', + }, + }, + actionTypeRegistry + ) + ).toHaveLength(0); + }); + + it('should validate incorrect action params', () => { + validateParamsMock.mockReturnValue({ + errors: ['Message is required'], + }); + + expect( + validateActionParams( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + actionTypeRegistry + ) + ).toHaveLength(1); + }); + + it('should validate incorrect action params and filter error objects', () => { + validateParamsMock.mockReturnValue({ + errors: [ + { + message: 'Message is required', + }, + ], + }); + + expect( + validateActionParams( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + actionTypeRegistry + ) + ).toHaveLength(0); + }); + + it('should validate incorrect action params and filter duplicated errors', () => { + validateParamsMock.mockReturnValue({ + errors: ['Message is required', 'Message is required', 'Message is required'], + }); + + expect( + validateActionParams( + { + id: '817b8bca-91d1-4729-8ee1-3a83aaafd9d4', + group: 'default', + actionTypeId: '.slack', + params: {}, + }, + actionTypeRegistry + ) + ).toHaveLength(1); + }); + }); +}); diff --git a/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.ts b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.ts new file mode 100644 index 0000000000000..ed80926d06ed6 --- /dev/null +++ b/x-pack/plugins/siem/public/alerts/components/rules/step_rule_actions/utils.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import mustache from 'mustache'; +import { uniq, startCase, flattenDeep, isArray, isString } from 'lodash/fp'; + +import { + AlertAction, + ActionTypeRegistryContract, +} from '../../../../../../triggers_actions_ui/public'; +import * as I18n from './translations'; + +const UUID_V4_REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; + +export const isUuidv4 = (id: AlertAction['id']) => !!id.match(UUID_V4_REGEX); + +export const getActionTypeName = (actionTypeId: AlertAction['actionTypeId']) => { + if (!actionTypeId) return ''; + const actionType = actionTypeId.split('.')[1]; + + if (!actionType) return ''; + + return startCase(actionType); +}; + +export const validateMustache = (params: AlertAction['params']) => { + const errors: string[] = []; + Object.entries(params).forEach(([paramKey, paramValue]) => { + if (!isString(paramValue)) return; + try { + mustache.render(paramValue, {}); + } catch (e) { + errors.push(I18n.INVALID_MUSTACHE_TEMPLATE(paramKey)); + } + }); + + return errors; +}; + +export const validateActionParams = ( + actionItem: AlertAction, + actionTypeRegistry: ActionTypeRegistryContract +): string[] => { + const actionErrors = actionTypeRegistry + .get(actionItem.actionTypeId) + ?.validateParams(actionItem.params); + + if (actionErrors) { + const actionErrorsValues = Object.values(actionErrors.errors); + + if (actionErrorsValues.length) { + const filteredObjects: Array = actionErrorsValues.filter( + item => isString(item) || isArray(item) + ) as Array; + const uniqActionErrors = uniq(flattenDeep(filteredObjects)); + + if (uniqActionErrors.length) { + return uniqActionErrors; + } + } + } + + 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 113612200d367..87cf9fb18adf3 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 5a483e0d5b876..eda6b0f28499c 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/graphql/types.ts b/x-pack/plugins/siem/public/graphql/types.ts index 3436ee84a2f30..c3493c580fa22 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 e6b063dfd2c07..21a37e31c6e80 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 e9cd78589fac9..586b9dec2f4ab 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 cfba40fc225a2..cda3a4b81ed9b 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 fbd2382e2826d..0b0d3bf43b1e9 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 6e159a792edb6..9bbde3d5236db 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 68b67db595d76..1b7d7994462c7 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 b0b863ebbbc0b..18e17a319883a 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 827167c63fd58..2d3fd75914822 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 a864667583c0a..abe601a546111 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 9a7cf5e2c2871..98cb2ef058485 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 82a6682e6461f..ade4d12517aca 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 85fb124464487..8f06e2c6e49b0 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 ff62ea4443f3f..9f9181359d44a 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 2a97c8a4a143e..dc0bd6cacf0d6 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 d6f21681df88f..a3338c878bd71 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 26441745a7f29..48ea2025b9b12 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 76f722274ce23..3aaff7e00ad51 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 76e3445358dd9..41c0faf4d608d 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 7b68dbcef2d7e..b640b449e6b8a 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/utils/build_validation/route_validation.ts b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts index 30b95dcfa94ee..19353080c15cb 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 ea6f5c80b9343..ad95c19870d15 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 d966d0c32651c..13a15c5711ba9 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 00ff3db976d66..c5d3c665a3b7f 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 782c261be9664..7f8acccf71d88 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 cec4bee1373ca..297df07636849 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 61c872ec9269c..92c78d63d1b2e 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 92b369807b0da..079cf2234b13b 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 d879a318fe815..a98f593f546a0 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 5d71980c83714..3156fae7545b1 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 563a569fe95b5..1411f80cecd2e 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 708dfcb70c67a..c81a18a3def87 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 3737377de2d5e..56be8d7bb7de7 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 7da991bc02b37..c8057a3e2fae1 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 1c01556abeae1..da47e42ea02a6 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": "他の構成", @@ -9977,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}", @@ -12829,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} ユーザーの削除", @@ -13305,8 +13298,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 3004b9787b1b1..3d96a0ebe0a3f 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": "其他配置", @@ -9983,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}", @@ -12836,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} 用户", @@ -13312,8 +13305,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 2f5172e8b386a..265cfddab4c06 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 4f5007949f8b1..1da9abea40dba 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 a72d8815c95b4..a0e8f3583ac43 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 99a3d65589e8e..016b564c47d00 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 bb0f21062c151..be00a030d5a27 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 fb40a42e47f75..5ae90a1613575 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 3ee949b9712bd..df9de8c2ad03d 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 fae81177a728c..33fbbb117a11a 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 cba96cd2fe5b1..0cdb3c0feb71f 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 = ({ ', () => { 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 f4b9441719386..8a6d2746237e9 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 94ef7af1c28d1..a9e07b80a9b22 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 6de21bc27d48c..6496c742fcb40 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 6a2e3b2e596b6..3f2891f919e37 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/tasks/build.ts b/x-pack/tasks/build.ts index 38ebb0bad4bdc..8dbf6d212d5d2 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 6e398f231a27c..f43b67e288561 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 78a04413daae7..0d990bff9f44e 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/functional/apps/reporting_management/report_delete_pagination.ts b/x-pack/test/functional/apps/reporting_management/report_delete_pagination.ts index d9d387f89640a..8c96fdbd0cbef 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/config.js b/x-pack/test/functional/config.js index 0639782dc7f7b..94976511a1b12 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/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index cb3598652a39a..d313fd2046c03 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/services/machine_learning/data_frame_analytics_creation.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts index 49f9b01c96895..d67f6bc946df2 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 37cc71d6865b0..d372bd53c081b 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 50de76d67e06b..f69c17c96f074 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/saved_object_api_integration/common/config.ts b/x-pack/test/saved_object_api_integration/common/config.ts index c6a8599b1797f..f933cb30ffd04 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 66bf3cf7b683b..275d80786e43c 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 044d259d5804e..a18f89cd480f8 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==