diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky index 3f77243da2c1bd..e1cbac0528b1f9 100644 --- a/.ci/Jenkinsfile_flaky +++ b/.ci/Jenkinsfile_flaky @@ -8,26 +8,28 @@ def JOB_PARTS = params.CI_GROUP.split(':') def IS_XPACK = JOB_PARTS[0] == 'xpack' def JOB = JOB_PARTS[1] def CI_GROUP = JOB_PARTS.size() > 2 ? JOB_PARTS[2] : '' +def EXECUTIONS = params.NUMBER_EXECUTIONS.toInteger() +def AGENT_COUNT = getAgentCount(EXECUTIONS) def worker = getWorkerFromParams(IS_XPACK, JOB, CI_GROUP) def workerFailures = [] currentBuild.displayName += trunc(" ${params.GITHUB_OWNER}:${params.branch_specifier}", 24) -currentBuild.description = "${params.CI_GROUP}
Executions: ${params.NUMBER_EXECUTIONS}" - -// Note: If you increase agent count, it will execute NUMBER_EXECUTIONS per agent. It will not divide them up amongst the agents -// e.g. NUMBER_EXECUTIONS = 25, agentCount = 4 results in 100 total executions -def agentCount = 1 +currentBuild.description = "${params.CI_GROUP}
Agents: ${AGENT_COUNT}
Executions: ${params.NUMBER_EXECUTIONS}" stage("Kibana Pipeline") { timeout(time: 180, unit: 'MINUTES') { timestamps { ansiColor('xterm') { def agents = [:] - for(def agentNumber = 1; agentNumber <= agentCount; agentNumber++) { + for(def agentNumber = 1; agentNumber <= AGENT_COUNT; agentNumber++) { + def agentNumberInside = agentNumber + def agentExecutions = floor(EXECUTIONS/AGENT_COUNT) + (agentNumber <= EXECUTIONS%AGENT_COUNT ? 1 : 0) agents["agent-${agentNumber}"] = { catchError { + print "Agent ${agentNumberInside} - ${agentExecutions} executions" + kibanaPipeline.withWorkers('flaky-test-runner', { if (!IS_XPACK) { kibanaPipeline.buildOss() @@ -37,7 +39,7 @@ stage("Kibana Pipeline") { } else { kibanaPipeline.buildXpack() } - }, getWorkerMap(agentNumber, params.NUMBER_EXECUTIONS.toInteger(), worker, workerFailures))() + }, getWorkerMap(agentNumberInside, agentExecutions, worker, workerFailures))() } } } @@ -77,9 +79,9 @@ def getWorkerFromParams(isXpack, job, ciGroup) { } } -def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWorkers = 14) { +def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWorkerProcesses = 12) { def workerMap = [:] - def numberOfWorkers = Math.min(numberOfExecutions, maxWorkers) + def numberOfWorkers = Math.min(numberOfExecutions, maxWorkerProcesses) for(def i = 1; i <= numberOfWorkers; i++) { def workerExecutions = numberOfExecutions/numberOfWorkers + (i <= numberOfExecutions%numberOfWorkers ? 1 : 0) @@ -87,7 +89,10 @@ def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWor workerMap["agent-${agentNumber}-worker-${i}"] = { workerNumber -> for(def j = 0; j < workerExecutions; j++) { print "Execute agent-${agentNumber} worker-${workerNumber}: ${j}" - withEnv(["JOB=agent-${agentNumber}-worker-${workerNumber}-${j}"]) { + withEnv([ + "JOB=agent-${agentNumber}-worker-${workerNumber}-${j}", + "REMOVE_KIBANA_INSTALL_DIR=1", + ]) { catchError { try { worker(workerNumber) @@ -104,6 +109,11 @@ def getWorkerMap(agentNumber, numberOfExecutions, worker, workerFailures, maxWor return workerMap } +def getAgentCount(executions) { + // Increase agent count every 24 worker processess, up to 3 agents maximum + return Math.min(3, 1 + floor(executions/24)) +} + def trunc(str, length) { if (str.size() >= length) { return str.take(length) + "..." @@ -111,3 +121,11 @@ def trunc(str, length) { return str; } + +// All of the real rounding/truncating methods are sandboxed +def floor(num) { + return num + .toString() + .split('\\.')[0] + .toInteger() +} diff --git a/.ci/jobs.yml b/.ci/jobs.yml index fc3e80064fa6fa..a2d8100f78efd0 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -14,7 +14,8 @@ JOB: - kibana-ciGroup10 - kibana-ciGroup11 - kibana-ciGroup12 - # - kibana-visualRegression + - kibana-accessibility + - kibana-visualRegression # make sure all x-pack-ciGroups are listed in test/scripts/jenkins_xpack_ci_group.sh - x-pack-firefoxSmoke @@ -28,7 +29,8 @@ JOB: - x-pack-ciGroup8 - x-pack-ciGroup9 - x-pack-ciGroup10 - # - x-pack-visualRegression + - x-pack-accessibility + - x-pack-visualRegression # `~` is yaml for `null` exclude: ~ diff --git a/.ci/run.sh b/.ci/run.sh index 88ce0bd9986a19..9f77438be62d0f 100755 --- a/.ci/run.sh +++ b/.ci/run.sh @@ -21,6 +21,9 @@ kibana-ciGroup*) kibana-visualRegression*) ./test/scripts/jenkins_visual_regression.sh ;; +kibana-accessibility*) + ./test/scripts/jenkins_accessibility.sh + ;; kibana-firefoxSmoke*) ./test/scripts/jenkins_firefox_smoke.sh ;; @@ -34,6 +37,9 @@ x-pack-ciGroup*) x-pack-visualRegression*) ./test/scripts/jenkins_xpack_visual_regression.sh ;; +x-pack-accessibility*) + ./test/scripts/jenkins_xpack_accessibility.sh + ;; x-pack-firefoxSmoke*) ./test/scripts/jenkins_xpack_firefox_smoke.sh ;; diff --git a/.eslintrc.js b/.eslintrc.js index adbf3398f105cc..daf49d9d08281d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -355,10 +355,10 @@ module.exports = { '!src/core/server/*.test.mocks.ts', 'src/plugins/**/public/**/*', - '!src/plugins/**/public/index*', + '!src/plugins/**/public/index.{js,ts,tsx}', 'src/plugins/**/server/**/*', - '!src/plugins/**/server/index*', + '!src/plugins/**/server/index.{js,ts,tsx}', ], allowSameFolder: true, }, @@ -393,6 +393,7 @@ module.exports = { 'x-pack/test/functional/apps/**/*.js', 'x-pack/legacy/plugins/apm/**/*.js', 'test/*/config.ts', + 'test/*/{tests,test_suites,apis,apps}/**/*', 'test/visual_regression/tests/**/*', 'x-pack/test/*/{tests,test_suites,apis,apps}/**/*', 'x-pack/test/*/*config.*ts', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 267691fde8197a..94296d076189bf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -44,7 +44,7 @@ /x-pack/test/functional/services/ml.ts @elastic/ml-ui # ML team owns the transform plugin, ES team added here for visibility # because the plugin lives in Kibana's Elasticsearch management section. -/x-pack/legacy/plugins/transform/ @elastic/ml-ui @elastic/es-ui +/x-pack/legacy/plugins/transform/ @elastic/ml-ui @elastic/es-ui # Operations /renovate.json5 @elastic/kibana-operations @@ -68,7 +68,9 @@ # Security /x-pack/legacy/plugins/security/ @elastic/kibana-security /x-pack/legacy/plugins/spaces/ @elastic/kibana-security +/x-pack/plugins/spaces/ @elastic/kibana-security /x-pack/legacy/plugins/encrypted_saved_objects/ @elastic/kibana-security +/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security /src/legacy/server/csp/ @elastic/kibana-security /x-pack/plugins/security/ @elastic/kibana-security /x-pack/test/api_integration/apis/security/ @elastic/kibana-security diff --git a/.i18nrc.json b/.i18nrc.json index 908cf192e7988e..01065201b9d641 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -1,39 +1,39 @@ { "paths": { "common.ui": "src/legacy/ui", - "data": ["src/legacy/core_plugins/data", "src/plugins/data"], - "expressions": "src/legacy/core_plugins/expressions", - "expressions_np": "src/plugins/expressions", - "kibana_react": "src/legacy/core_plugins/kibana_react", - "navigation": "src/legacy/core_plugins/navigation", - "server": "src/legacy/server", "console": "src/legacy/core_plugins/console", "core": "src/core", + "dashboardEmbeddableContainer": "src/plugins/dashboard_embeddable_container", + "data": ["src/legacy/core_plugins/data", "src/plugins/data"], + "embeddableApi": "src/plugins/embeddable", + "esUi": "src/plugins/es_ui_shared", + "expressions_np": "src/plugins/expressions", + "expressions": "src/legacy/core_plugins/expressions", "inputControl": "src/legacy/core_plugins/input_control_vis", + "inspector": "src/plugins/inspector", "inspectorViews": "src/legacy/core_plugins/inspector_views", "interpreter": "src/legacy/core_plugins/interpreter", - "dashboardEmbeddableContainer": "src/legacy/core_plugins/dashboard_embeddable_container", "kbn": "src/legacy/core_plugins/kibana", "kbnDocViews": "src/legacy/core_plugins/kbn_doc_views", - "embeddableApi": "src/plugins/embeddable", + "kbnESQuery": "packages/kbn-es-query", "kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types", - "visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown", - "visTypeMetric": "src/legacy/core_plugins/vis_type_metric", - "visTypeVega": "src/legacy/core_plugins/vis_type_vega", - "visTypeTable": "src/legacy/core_plugins/vis_type_table", + "kibana_react": "src/legacy/core_plugins/kibana_react", + "kibana-react": "src/plugins/kibana_react", + "navigation": "src/legacy/core_plugins/navigation", "regionMap": "src/legacy/core_plugins/region_map", + "server": "src/legacy/server", "statusPage": "src/legacy/core_plugins/status_page", + "telemetry": "src/legacy/core_plugins/telemetry", "tileMap": "src/legacy/core_plugins/tile_map", "timelion": "src/legacy/core_plugins/timelion", + "uiActions": "src/plugins/ui_actions", + "visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown", + "visTypeMetric": "src/legacy/core_plugins/vis_type_metric", + "visTypeTable": "src/legacy/core_plugins/vis_type_table", "visTypeTagCloud": "src/legacy/core_plugins/vis_type_tagcloud", "visTypeTimeseries": "src/legacy/core_plugins/vis_type_timeseries", - "kbnESQuery": "packages/kbn-es-query", - "inspector": "src/plugins/inspector", - "kibana-react": "src/plugins/kibana_react", - "visualizations": "src/plugins/visualizations", - "telemetry": "src/legacy/core_plugins/telemetry", - "esUi": "src/plugins/es_ui_shared", - "uiActions": "src/plugins/ui_actions" + "visTypeVega": "src/legacy/core_plugins/vis_type_vega", + "visualizations": "src/plugins/visualizations" }, "exclude": ["src/legacy/ui/ui_render/ui_render_mixin.js"], "translations": [] diff --git a/Jenkinsfile b/Jenkinsfile index 4820de98767376..8d8579736f6392 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,7 +25,8 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), 'oss-firefoxSmoke': kibanaPipeline.getPostBuildWorker('firefoxSmoke', { runbld './test/scripts/jenkins_firefox_smoke.sh' }), - // 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), + 'oss-accessibility': kibanaPipeline.getPostBuildWorker('accessibility', { runbld './test/scripts/jenkins_accessibility.sh' }), + 'oss-visualRegression': kibanaPipeline.getPostBuildWorker('visualRegression', { runbld './test/scripts/jenkins_visual_regression.sh' }), ]), 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), @@ -39,7 +40,8 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), 'xpack-firefoxSmoke': kibanaPipeline.getPostBuildWorker('xpack-firefoxSmoke', { runbld './test/scripts/jenkins_xpack_firefox_smoke.sh' }), - // 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), + 'xpack-accessibility': kibanaPipeline.getPostBuildWorker('xpack-accessibility', { runbld './test/scripts/jenkins_xpack_accessibility.sh' }), + 'xpack-visualRegression': kibanaPipeline.getPostBuildWorker('xpack-visualRegression', { runbld './test/scripts/jenkins_xpack_visual_regression.sh' }), ]), ]) } diff --git a/docs/canvas/canvas-expressions.asciidoc b/docs/canvas/canvas-expressions.asciidoc deleted file mode 100644 index 0fcc69c6db726e..00000000000000 --- a/docs/canvas/canvas-expressions.asciidoc +++ /dev/null @@ -1,39 +0,0 @@ -[[canvas-expression-editor]] -=== Customize your element with the expression editor - -Each element is backed by an expression that represents the element style and data. To further define the appearance and behavior of the element, use the expression editor. - -. In the lower right corner, click *Expression editor*. - -. Edit the style and data parts of the expression that you want to change. - -. Click *Run*. - -. If you like what you see, click *Close*. - -For information about the Canvas expression language, see <>. - -//Insert expression video. - -[float] -[[canvas-expression-editor-example]] -=== Example: Using the expression editor - -Build a complex element using expressions. - -``` -image mode="contain" dataurl={ -asset { -filters | essql -query="SELECT host,response -FROM kibana_sample_data_logs -WHERE host='artifacts.elastic.co' -ORDER BY timestamp DESC -LIMIT 1"| -alterColumn "response" type="number" | -getCell "response" | -if {compare lt to=400} then="asset-0a807073-d056-4c7b-9bf4-225b71e47243" else="asset-1343672d-7c02-4402-929e-0f8fef69cddd" -} -} | render - -``` \ No newline at end of file diff --git a/docs/canvas/canvas-share-workpad.asciidoc b/docs/canvas/canvas-share-workpad.asciidoc index 5cc5b953e5775f..119ede16fe1e7b 100644 --- a/docs/canvas/canvas-share-workpad.asciidoc +++ b/docs/canvas/canvas-share-workpad.asciidoc @@ -2,47 +2,100 @@ [[workpad-share-options]] == Share your workpad -When you are ready to share your workpad, create a PDF, or export your workpad. +When you've finished your workpad, you can share it outside of {kib}. + +[float] +[[export-single-workpad]] +=== Export workpads + +Create a JSON file of your workpad that you can export outside of {kib}. + +. From your workpad, click the *Share workpad* icon in the upper left corner. + +. Select *Download as JSON*. ++ +[role="screenshot"] +image::images/canvas-export-workpad.png[Export single workpad] + +Want to export multiple workpads? Go to the *Canvas workpads* view, select the workpads you want to export, then click *Export*. [float] [[create-workpad-pdf]] === Create a PDF -To view your workpad outside of Kibana, generate a PDF. +Create a PDF copy of your workpad that you can save and share outside of {kib}. . If you are using a Gold or Platinum license, enable reporting in your `config/kibana.yml` file. . From your workpad, click the *Share workpad* icon in the upper left corner, then select *PDF reports*. -. Click *Generate PDF*. +. Click *Generate PDF*. + [role="screenshot"] image::images/canvas-generate-pdf.gif[Generate PDF] [float] -[[export-workpad]] -=== Export your workpad +[[create-workpad-URL]] +=== Create a POST URL + +Create a POST URL that you can use to automatically generate PDF reports using Watcher or a script. + +For more information, refer to <>. + +. If you are using a Gold or Platinum license, enable reporting in your `config/kibana.yml` file. + +. From your workpad, click the *Share workpad* icon in the upper left corner, then select *PDF reports*. -To share your workpad with another author, export it as a JSON file. +. Click *Copy POST URL*. ++ +[role="screenshot"] +image::images/canvas-create-URL.gif[Create POST URL] [float] -[[export-single-workpad]] -==== Export a single workpad +[[add-workpad-website]] +=== Share the workpad on a website -. From your workpad, click the *Share workpad* icon in the upper left corner. +beta[] Download the workpad and share it on any website, then customize the workpad behavior to autoplay the pages or hide the toolbar. -. Select *Download as JSON*. +. If you are using a Gold or Platinum license, enable reporting in your `config/kibana.yml` file. + +. From your workpad, click the *Share this workpad* icon in the upper left corner, then select *Share on a website*. + +. On the *Share on a website* pane, follow the instructions. + +. To customize the workpad behavior to autoplay the pages or hide the toolbar, use the inline parameters. ++ +To make sure that your data remains secure, the data in the JSON file is not connected to {kib}. Canvas does not display elements that manipulate the data on the workpad. + [role="screenshot"] -image::images/canvas-export-workpad.png[Export single workpad] +image::images/canvas-embed_workpad.gif[Share the workpad on a website] ++ +NOTE: Shareable workpads encode the current state of the workpad in a JSON file. When you make changes to the workpad, the changes do not appear in the shareable workpad on your website. [float] -[[export-multiple-workpads]] -==== Export multiple workpads +[[change-the-workpad-settings]] +=== Change the shareable workpad settings -. Go to the *Canvas workpads* page. +After you've added the workpad to your website, you can change the autoplay and toolbar settings. -. Select the workpads you want to export +[float] +[[shareable-workpad-enable-autoplay]] +==== Change the autoplay settings -. Click *Export*. +. In the lower right corner of the shareable workpad, click the settings icon. +. Click *Auto Play*, then change the settings. ++ +[role="screenshot"] +image::images/canvas_share_autoplay_480.gif[Autoplay settings] + +[float] +[[hide-workpad-toolbar]] +==== Change the toolbar settings + +. In the lower right corner, click the settings icon. + +. Click *Toolbar*, then change the settings. ++ +[role="screenshot"] +image::images/canvas_share_hidetoolbar_480.gif[Hide toolbar settings] diff --git a/docs/canvas/canvas-workpad.asciidoc b/docs/canvas/canvas-workpad.asciidoc index f664f055a5ff65..f833bd903b0bca 100644 --- a/docs/canvas/canvas-workpad.asciidoc +++ b/docs/canvas/canvas-workpad.asciidoc @@ -18,17 +18,17 @@ To create a workpad, you can: [[blank-canvas-workpad]] === Start with a blank page -To use the background colors, images, and data of your choice, start with a blank workpad. +To use the background colors, images, and data of your choice, start with a blank workpad. . Open *Canvas*. -. On the *Canvas workpads* page, click *Create workpad*. +. On the *Canvas workpads* view, click *Create workpad*. . Add a *Name* to your workpad. -. In the *Width* and *Height* fields, specify the size. +. In the *Width* and *Height* fields, specify the size. -. Select the layout. +. Select the layout. + For example, click *720p* for a traditional presentation layout. @@ -45,7 +45,7 @@ If you're unsure about where to start, you can use one of the preconfigured temp . Open *Canvas*. -. On the *Canvas workpads* page, select *Templates*. +. On the *Canvas workpads* view, select *Templates*. . Click the preconfigured template that you want to use. @@ -59,7 +59,7 @@ When you want to use a workpad that someone else has already started, import the . Open *Canvas*. -. On the *Canvas workpads* page, click and drag the file to the *Import workpad JSON file* field. +. On the *Canvas workpads* view, click and drag the file to the *Import workpad JSON file* field. [float] [[sample-data-workpad]] @@ -67,11 +67,11 @@ When you want to use a workpad that someone else has already started, import the Each of the sample data sets comes with a Canvas workpad that you can use for your own workpad inspiration. -. Add a {kibana-ref}/add-sample-data.html[sample data set]. +. Add a {kibana-ref}/add-sample-data.html[sample data set]. . On the *Add Data to Kibana* page, click the *View data* dropdown list, then select *Canvas*. + -Need some more workpad inspiration? Check out the link:https://www.elastic.co/blog/[Elastic Blog]. +Need some more workpad inspiration? Check out the link:https://www.elastic.co/blog/[Elastic Blog]. [float] [[apply-workpad-styles]] diff --git a/docs/images/canvas-create-URL.gif b/docs/images/canvas-create-URL.gif new file mode 100644 index 00000000000000..c7dfab28c681e6 Binary files /dev/null and b/docs/images/canvas-create-URL.gif differ diff --git a/docs/images/canvas-embed_workpad.gif b/docs/images/canvas-embed_workpad.gif new file mode 100644 index 00000000000000..0ffbdeab2862d6 Binary files /dev/null and b/docs/images/canvas-embed_workpad.gif differ diff --git a/docs/images/canvas-export-workpad.png b/docs/images/canvas-export-workpad.png index 687c8a121b65ad..fa910daf948d76 100644 Binary files a/docs/images/canvas-export-workpad.png and b/docs/images/canvas-export-workpad.png differ diff --git a/docs/images/canvas-generate-pdf.gif b/docs/images/canvas-generate-pdf.gif index 6d60f948de77a5..a51e04cb623a6b 100644 Binary files a/docs/images/canvas-generate-pdf.gif and b/docs/images/canvas-generate-pdf.gif differ diff --git a/docs/images/canvas_share_autoplay_480.gif b/docs/images/canvas_share_autoplay_480.gif new file mode 100644 index 00000000000000..da6d8c74dd5ca9 Binary files /dev/null and b/docs/images/canvas_share_autoplay_480.gif differ diff --git a/docs/images/canvas_share_hidetoolbar_480.gif b/docs/images/canvas_share_hidetoolbar_480.gif new file mode 100644 index 00000000000000..6206714aa28048 Binary files /dev/null and b/docs/images/canvas_share_hidetoolbar_480.gif differ diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index a8d8a66fd182c7..af67ff70c81b59 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -35,3 +35,7 @@ This page has moved. Please see <>. This page has moved. Please see <>. +[role="exclude",id="extend"] +== Extend your use case + +This page was deleted. See <> and <>. \ No newline at end of file diff --git a/docs/user/canvas.asciidoc b/docs/user/canvas.asciidoc index f3aa78e8e2e670..c58635ba977696 100644 --- a/docs/user/canvas.asciidoc +++ b/docs/user/canvas.asciidoc @@ -5,15 +5,15 @@ [partintro] -- -Canvas is a data visualization and presentation tool that sits within Kibana. With Canvas, you can pull live data directly from Elasticsearch, and combine the data with colors, images, text, and your imagination to create dynamic, multi-page, pixel-perfect displays. If you are a little bit creative, a little bit technical, and a whole lot curious, then Canvas is for you. +Canvas is a data visualization and presentation tool that sits within Kibana. With Canvas, you can pull live data directly from Elasticsearch, and combine the data with colors, images, text, and your imagination to create dynamic, multi-page, pixel-perfect displays. If you are a little bit creative, a little bit technical, and a whole lot curious, then Canvas is for you. With Canvas, you can: -* Create and personalize your work space with backgrounds, borders, colors, fonts, and more. +* Create and personalize your work space with backgrounds, borders, colors, fonts, and more. * Customize your workpad with your own visualizations, such as images and text. -* Customize your data by pulling it directly from Elasticsearch. +* Customize your data by pulling it directly from Elasticsearch. * Show off your data with charts, graphs, progress monitors, and more. @@ -37,8 +37,6 @@ include::{kib-repo-dir}/canvas/canvas-present-workpad.asciidoc[] include::{kib-repo-dir}/canvas/canvas-share-workpad.asciidoc[] -//include::{kib-repo-dir}/canvas/canvas-expressions.asciidoc[] - include::{kib-repo-dir}/canvas/canvas-function-reference.asciidoc[] include::{kib-repo-dir}/canvas/canvas-tinymath-functions.asciidoc[] diff --git a/docs/user/extend.asciidoc b/docs/user/extend.asciidoc deleted file mode 100644 index c658731ce3c3db..00000000000000 --- a/docs/user/extend.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -[[extend]] -= Extend your use case - -[partintro] --- -//TBD - -* <> -* <> - --- - -include::graph/index.asciidoc[] - -include::ml/index.asciidoc[] diff --git a/docs/user/graph/configuring-graph.asciidoc b/docs/user/graph/configuring-graph.asciidoc index 9503e606406b35..d521f9d8d2846d 100644 --- a/docs/user/graph/configuring-graph.asciidoc +++ b/docs/user/graph/configuring-graph.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[graph-configuration]] -=== Configuring Graph +== Configuring Graph When a user saves a graph workspace in Kibana, it is stored in the `.kibana` index along with other saved objects like visualizations and dashboards. @@ -57,9 +57,9 @@ is displayed. For more information on granting access to Kibana, see [role="screenshot"] image::user/graph/images/graph-read-only-badge.png[Example of Graph's read only access indicator in Kibana's header] -[float] +[discrete] [[disable-drill-down]] -==== Disabling drill down configuration +=== Disabling drill down configuration By default, users can configure _drill down_ URLs to display additional information about a selected vertex in a new browser window. For example, diff --git a/docs/user/graph/getting-started.asciidoc b/docs/user/graph/getting-started.asciidoc index dd5e8527c8976f..19f3df341338ec 100644 --- a/docs/user/graph/getting-started.asciidoc +++ b/docs/user/graph/getting-started.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[graph-getting-started]] -=== Using Graph +== Using Graph Graph is automatically enabled in {es} and {kib}. diff --git a/docs/user/graph/index.asciidoc b/docs/user/graph/index.asciidoc index 9ca7b0e4b1a4a4..f9094f5b594b10 100644 --- a/docs/user/graph/index.asciidoc +++ b/docs/user/graph/index.asciidoc @@ -1,7 +1,9 @@ [role="xpack"] [[xpack-graph]] -== Graph data connections += Graph data connections +[partintro] +-- The {graph-features} enable you to discover how items in an Elasticsearch index are related. You can explore the connections between indexed terms and see which connections are the most meaningful. This can be @@ -17,9 +19,9 @@ and an interactive graph visualization tool for Kibana. Both work out of the box with existing Elasticsearch indices--you don't need to store any additional data to use these features. +[discrete] [[how-graph-works]] -[float] -=== How Graph works +== How Graph works The graph API provides an alternative way to extract and summarize information about the documents and terms in your Elasticsearch index. A _graph_ is really just a network of related items. In our case, this means a network of related @@ -62,6 +64,7 @@ multi-node clusters and scales with your Elasticsearch deployment. Advanced options let you control how your data is sampled and summarized. You can also set timeouts to prevent graph queries from adversely affecting the cluster. +-- include::getting-started.asciidoc[] diff --git a/docs/user/graph/limitations.asciidoc b/docs/user/graph/limitations.asciidoc index b40f15000483ad..e96910bd27b4c0 100644 --- a/docs/user/graph/limitations.asciidoc +++ b/docs/user/graph/limitations.asciidoc @@ -1,12 +1,12 @@ [role="xpack"] [[graph-limitations]] -=== Graph limitations +== Graph limitations ++++ Limitations ++++ -[float] -==== Limited support for multiple indices +[discrete] +=== Limited support for multiple indices The graph API can explore multiple indices, types, or aliases in a single API request, but the assumption is that each "hop" it performs is querying the same set of indices. Currently, it is not possible to diff --git a/docs/user/graph/troubleshooting.asciidoc b/docs/user/graph/troubleshooting.asciidoc index 7a87aba7b7f813..ff3568ed41afa2 100644 --- a/docs/user/graph/troubleshooting.asciidoc +++ b/docs/user/graph/troubleshooting.asciidoc @@ -1,12 +1,12 @@ [role="xpack"] [[graph-troubleshooting]] -=== Graph Troubleshooting +== Graph Troubleshooting ++++ Troubleshooting ++++ -[float] -==== Why are results missing? +[discrete] +=== Why are results missing? The default settings in Graph API requests are configured to tune out noisy results by using the following strategies: @@ -29,8 +29,8 @@ of any statistical correlation with the sample. * Set the `min_doc_count` for your vertices to 1 to ensure only one document is required to assert a relationship. -[float] -==== What can I do to to improve performance? +[discrete] +=== What can I do to to improve performance? With the default setting of `use_significance` set to `true`, the Graph API performs a background frequency check of the terms it discovers as part of diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index 4b29255c50a1d8..ebe6c10c498724 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -16,7 +16,9 @@ include::dashboard.asciidoc[] include::canvas.asciidoc[] -include::extend.asciidoc[] +include::graph/index.asciidoc[] + +include::ml/index.asciidoc[] include::{kib-repo-dir}/maps/index.asciidoc[] diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index a5f14ed2cf944e..b7bf459c39d984 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -1,7 +1,9 @@ [role="xpack"] [[xpack-ml]] -== {ml-cap} += {ml-cap} +[partintro] +-- As datasets increase in size and complexity, the human effort required to inspect dashboards or maintain rules for spotting infrastructure problems, cyber attacks, or business issues becomes impractical. Elastic {ml-features} @@ -28,10 +30,10 @@ You need the following permissions to use the Data Visualizer with file upload: For more information, see {ref}/security-privileges.html[Security privileges] and {ref}/built-in-roles.html[Built-in roles]. +-- -[float] [[xpack-ml-anomalies]] -=== {anomaly-detect-cap} +== {anomaly-detect-cap} The Elastic {ml} {anomaly-detect} feature automatically models the normal behavior of your time series data — learning trends, periodicity, and more — in @@ -82,9 +84,8 @@ For more information about the {anomaly-detect} feature, see https://www.elastic.co/what-is/elastic-stack-machine-learning[{ml-cap} in the {stack}] and {stack-ov}/xpack-ml.html[{ml-cap} {anomaly-detect}]. -[float] [[xpack-ml-dfanalytics]] -=== {dfanalytics-cap} +== {dfanalytics-cap} The Elastic {ml} {dfanalytics} feature enables you to analyze your data using {oldetection} and {regression} algorithms and generate new indices that contain @@ -98,4 +99,4 @@ in {kib}. For example: image::user/ml/images/outliers.jpg[{oldetection-cap} results in {kib}] For more information about the {dfanalytics} feature, see -{stack-ov}/ml-dfanalytics.html[{ml-cap} {dfanalytics}]. +{stack-ov}/ml-dfanalytics.html[{ml-cap} {dfanalytics}]. \ No newline at end of file diff --git a/package.json b/package.json index 4a3b3bfdc29852..a8e60e9749f724 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "dependencies": { "@babel/core": "^7.5.5", "@babel/register": "^7.5.5", - "@elastic/charts": "^13.5.9", + "@elastic/charts": "^13.5.12", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", "@elastic/eui": "14.8.0", @@ -156,8 +156,8 @@ "d3": "3.5.17", "d3-cloud": "1.2.5", "del": "^5.1.0", - "elasticsearch": "^16.4.0", - "elasticsearch-browser": "^16.4.0", + "elasticsearch": "^16.5.0", + "elasticsearch-browser": "^16.5.0", "encode-uri-query": "1.0.1", "execa": "^3.2.0", "expiry-js": "0.1.7", @@ -233,7 +233,7 @@ "reselect": "^3.0.1", "resize-observer-polyfill": "^1.5.0", "rison-node": "1.0.2", - "rxjs": "^6.2.1", + "rxjs": "^6.5.3", "script-loader": "0.7.2", "semver": "^5.5.0", "style-it": "^2.1.3", @@ -350,6 +350,7 @@ "@typescript-eslint/parser": "^2.5.0", "angular-mocks": "^1.7.8", "archiver": "^3.1.1", + "axe-core": "^3.3.2", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", "babel-plugin-dynamic-import-node": "^2.3.0", @@ -359,7 +360,7 @@ "chance": "1.0.18", "cheerio": "0.22.0", "chokidar": "3.2.1", - "chromedriver": "^77.0.0", + "chromedriver": "^78.0.1", "classnames": "2.2.6", "dedent": "^0.7.0", "delete-empty": "^2.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 5ef4219df59f75..793044e3796ea0 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -18,6 +18,25 @@ */ module.exports = (_, options = {}) => { + const overrides = []; + if (!process.env.ALLOW_PERFORMANCE_HOOKS_IN_TASK_MANAGER) { + overrides.push( + { + test: [/x-pack[\/\\]legacy[\/\\]plugins[\/\\]task_manager/], + plugins: [ + [ + require.resolve('babel-plugin-filter-imports'), + { + imports: { + perf_hooks: ['performance'], + }, + }, + ], + ], + } + ); + } + return { presets: [ [ @@ -39,7 +58,7 @@ module.exports = (_, options = {}) => { modules: 'cjs', corejs: 3, - ...(options['@babel/preset-env'] || {}) + ...(options['@babel/preset-env'] || {}), }, ], require('./common_preset'), @@ -48,9 +67,10 @@ module.exports = (_, options = {}) => { [ require.resolve('babel-plugin-transform-define'), { - 'global.__BUILT_WITH_BABEL__': 'true' - } - ] - ] + 'global.__BUILT_WITH_BABEL__': 'true', + }, + ], + ], + overrides, }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 4b183577453606..c22cf175b29e59 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -8,10 +8,11 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-modules-commonjs": "^7.5.0", "@babel/preset-env": "^7.5.5", - "@babel/preset-react":"^7.0.0", + "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", "@kbn/elastic-idx": "1.0.0", "babel-plugin-add-module-exports": "^1.0.2", + "babel-plugin-filter-imports": "^3.0.0", "babel-plugin-transform-define": "^1.3.1", "babel-plugin-typescript-strip-namespaces": "^1.1.1" } diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a5789a0a105b3b..e8781f6d901d92 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -16,7 +16,7 @@ "exit-hook": "^2.2.0", "getopts": "^2.2.5", "moment": "^2.24.0", - "rxjs": "^6.2.1", + "rxjs": "^6.5.3", "tree-kill": "^1.2.1", "tslib": "^1.9.3" }, diff --git a/packages/kbn-dev-utils/src/proc_runner/proc.ts b/packages/kbn-dev-utils/src/proc_runner/proc.ts index dab69de47af615..c899293191f2a7 100644 --- a/packages/kbn-dev-utils/src/proc_runner/proc.ts +++ b/packages/kbn-dev-utils/src/proc_runner/proc.ts @@ -100,9 +100,9 @@ export function startProc(name: string, options: ProcOptions, log: ToolingLog) { const outcome$: Rx.Observable = Rx.race( // observe first exit event - Rx.fromEvent(childProcess, 'exit').pipe( + Rx.fromEvent<[number]>(childProcess, 'exit').pipe( take(1), - map(([code]: [number]) => { + map(([code]) => { if (stopCalled) { return null; } diff --git a/packages/kbn-es-query/src/es_query/migrate_filter.js b/packages/kbn-es-query/src/es_query/migrate_filter.js index d5f52648b027e2..b74fc485a6184f 100644 --- a/packages/kbn-es-query/src/es_query/migrate_filter.js +++ b/packages/kbn-es-query/src/es_query/migrate_filter.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { getConvertedValueForField } from '../filters'; +import { getConvertedValueForField } from '../utils/filters'; export function migrateFilter(filter, indexPattern) { if (filter.match) { diff --git a/packages/kbn-es-query/src/filters/__tests__/phrase.js b/packages/kbn-es-query/src/filters/__tests__/phrase.js deleted file mode 100644 index dbd794a018d9ea..00000000000000 --- a/packages/kbn-es-query/src/filters/__tests__/phrase.js +++ /dev/null @@ -1,92 +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. - */ - - -import { buildInlineScriptForPhraseFilter, buildPhraseFilter } from '../phrase'; -import expect from '@kbn/expect'; -import _ from 'lodash'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; -import filterSkeleton from '../../__fixtures__/filter_skeleton'; - -let expected; - -describe('Filter Manager', function () { - describe('Phrase filter builder', function () { - beforeEach(() => { - expected = _.cloneDeep(filterSkeleton); - }); - - it('should be a function', function () { - expect(buildPhraseFilter).to.be.a(Function); - }); - - it('should return a match query filter when passed a standard field', function () { - const field = getField(indexPattern, 'bytes'); - expected.query = { - match_phrase: { - bytes: 5 - } - }; - expect(buildPhraseFilter(field, 5, indexPattern)).to.eql(expected); - }); - - it('should return a script filter when passed a scripted field', function () { - const field = getField(indexPattern, 'script number'); - expected.meta.field = 'script number'; - _.set(expected, 'script.script', { - source: '(' + field.script + ') == value', - lang: 'expression', - params: { - value: 5, - } - }); - expect(buildPhraseFilter(field, 5, indexPattern)).to.eql(expected); - }); - }); - - describe('buildInlineScriptForPhraseFilter', function () { - - it('should wrap painless scripts in a lambda', function () { - const field = { - lang: 'painless', - script: 'return foo;', - }; - - const expected = `boolean compare(Supplier s, def v) {return s.get() == v;}` + - `compare(() -> { return foo; }, params.value);`; - - expect(buildInlineScriptForPhraseFilter(field)).to.be(expected); - }); - - it('should create a simple comparison for other langs', function () { - const field = { - lang: 'expression', - script: 'doc[bytes].value', - }; - - const expected = `(doc[bytes].value) == value`; - - expect(buildInlineScriptForPhraseFilter(field)).to.be(expected); - }); - }); -}); - -function getField(indexPattern, name) { - return indexPattern.fields.find(field => field.name === name); -} diff --git a/packages/kbn-es-query/src/filters/__tests__/query.js b/packages/kbn-es-query/src/filters/__tests__/query.js deleted file mode 100644 index 8b774f05c29d38..00000000000000 --- a/packages/kbn-es-query/src/filters/__tests__/query.js +++ /dev/null @@ -1,46 +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. - */ - -import { buildQueryFilter } from '../query'; -import { cloneDeep } from 'lodash'; -import expect from '@kbn/expect'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; -import filterSkeleton from '../../__fixtures__/filter_skeleton'; - -let expected; - -describe('Filter Manager', function () { - describe('Phrase filter builder', function () { - beforeEach(() => { - expected = cloneDeep(filterSkeleton); - }); - - it('should be a function', function () { - expect(buildQueryFilter).to.be.a(Function); - }); - - it('should return a query filter when passed a standard field', function () { - expected.query = { - foo: 'bar' - }; - expect(buildQueryFilter({ foo: 'bar' }, indexPattern.id)).to.eql(expected); - }); - - }); -}); diff --git a/packages/kbn-es-query/src/filters/__tests__/range.js b/packages/kbn-es-query/src/filters/__tests__/range.js deleted file mode 100644 index 9b23fc23908d4d..00000000000000 --- a/packages/kbn-es-query/src/filters/__tests__/range.js +++ /dev/null @@ -1,156 +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. - */ - -import { buildRangeFilter } from '../range'; -import expect from '@kbn/expect'; -import _ from 'lodash'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; -import filterSkeleton from '../../__fixtures__/filter_skeleton'; - -let expected; - -describe('Filter Manager', function () { - describe('Range filter builder', function () { - beforeEach(() => { - expected = _.cloneDeep(filterSkeleton); - }); - - it('should be a function', function () { - expect(buildRangeFilter).to.be.a(Function); - }); - - it('should return a range filter when passed a standard field', function () { - const field = getField(indexPattern, 'bytes'); - expected.range = { - bytes: { - gte: 1, - lte: 3 - } - }; - expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).to.eql(expected); - }); - - it('should return a script filter when passed a scripted field', function () { - const field = getField(indexPattern, 'script number'); - expected.meta.field = 'script number'; - _.set(expected, 'script.script', { - lang: 'expression', - source: '(' + field.script + ')>=gte && (' + field.script + ')<=lte', - params: { - value: '>=1 <=3', - gte: 1, - lte: 3 - } - }); - expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).to.eql(expected); - }); - - it('should wrap painless scripts in comparator lambdas', function () { - const field = getField(indexPattern, 'script date'); - const expected = `boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))} ` + - `boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}` + - `gte(() -> { ${field.script} }, params.gte) && ` + - `lte(() -> { ${field.script} }, params.lte)`; - - const inlineScript = buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern).script.script.source; - expect(inlineScript).to.be(expected); - }); - - it('should throw an error when gte and gt, or lte and lt are both passed', function () { - const field = getField(indexPattern, 'script number'); - expect(function () { - buildRangeFilter(field, { gte: 1, gt: 3 }, indexPattern); - }).to.throwError(); - expect(function () { - buildRangeFilter(field, { lte: 1, lt: 3 }, indexPattern); - }).to.throwError(); - }); - - it('to use the right operator for each of gte, gt, lt and lte', function () { - const field = getField(indexPattern, 'script number'); - _.each({ gte: '>=', gt: '>', lte: '<=', lt: '<' }, function (operator, key) { - const params = {}; - params[key] = 5; - const filter = buildRangeFilter(field, params, indexPattern); - - expect(filter.script.script.source).to.be( - '(' + field.script + ')' + operator + key); - expect(filter.script.script.params[key]).to.be(5); - expect(filter.script.script.params.value).to.be(operator + 5); - - }); - }); - - describe('when given params where one side is infinite', function () { - const field = getField(indexPattern, 'script number'); - let filter; - beforeEach(function () { - filter = buildRangeFilter(field, { gte: 0, lt: Infinity }, indexPattern); - }); - - describe('returned filter', function () { - it('is a script filter', function () { - expect(filter).to.have.property('script'); - }); - - it('contain a param for the finite side', function () { - expect(filter.script.script.params).to.have.property('gte', 0); - }); - - it('does not contain a param for the infinite side', function () { - expect(filter.script.script.params).not.to.have.property('lt'); - }); - - it('does not contain a script condition for the infinite side', function () { - const field = getField(indexPattern, 'script number'); - const script = field.script; - expect(filter.script.script.source).to.equal(`(${script})>=gte`); - }); - }); - }); - - describe('when given params where both sides are infinite', function () { - const field = getField(indexPattern, 'script number'); - let filter; - beforeEach(function () { - filter = buildRangeFilter( - field, { gte: -Infinity, lt: Infinity }, indexPattern); - }); - - describe('returned filter', function () { - it('is a match_all filter', function () { - expect(filter).not.to.have.property('script'); - expect(filter).to.have.property('match_all'); - }); - - it('does not contain params', function () { - expect(filter).not.to.have.property('params'); - }); - - it('meta field is set to field name', function () { - expect(filter.meta.field).to.equal('script number'); - }); - }); - }); - }); -}); - -function getField(indexPattern, name) { - return indexPattern.fields.find(field => field.name === name); -} diff --git a/packages/kbn-es-query/src/filters/index.d.ts b/packages/kbn-es-query/src/filters/index.d.ts deleted file mode 100644 index c05e32dbf07b95..00000000000000 --- a/packages/kbn-es-query/src/filters/index.d.ts +++ /dev/null @@ -1,51 +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. - */ - -import { CustomFilter, ExistsFilter, PhraseFilter, PhrasesFilter, RangeFilter } from './lib'; -import { RangeFilterParams } from './lib/range_filter'; - -export * from './lib'; - -// We can't import the real types from the data plugin, so need to either duplicate -// them here or figure out another solution, perhaps housing them in this package -type Field = any; -type IndexPattern = any; - -export function buildExistsFilter(field: Field, indexPattern: IndexPattern): ExistsFilter; - -export function buildPhraseFilter( - field: Field, - value: string, - indexPattern: IndexPattern -): PhraseFilter; - -export function buildPhrasesFilter( - field: Field, - values: string[], - indexPattern: IndexPattern -): PhrasesFilter; - -export function buildQueryFilter(query: any, index: string, alias?: string): CustomFilter; - -export function buildRangeFilter( - field: Field, - params: RangeFilterParams, - indexPattern: IndexPattern, - formattedValue?: string -): RangeFilter; diff --git a/packages/kbn-es-query/src/filters/index.js b/packages/kbn-es-query/src/filters/index.js deleted file mode 100644 index d7d092eabd8a2f..00000000000000 --- a/packages/kbn-es-query/src/filters/index.js +++ /dev/null @@ -1,25 +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. - */ - -export * from './exists'; -export * from './phrase'; -export * from './phrases'; -export * from './query'; -export * from './range'; -export * from './lib'; diff --git a/packages/kbn-es-query/src/filters/lib/index.ts b/packages/kbn-es-query/src/filters/lib/index.ts deleted file mode 100644 index ea023987103414..00000000000000 --- a/packages/kbn-es-query/src/filters/lib/index.ts +++ /dev/null @@ -1,95 +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. - */ - -// The interface the other filters extend -export * from './meta_filter'; - -// The actual filter types -import { CustomFilter } from './custom_filter'; -import { ExistsFilter, isExistsFilter } from './exists_filter'; -import { GeoBoundingBoxFilter, isGeoBoundingBoxFilter } from './geo_bounding_box_filter'; -import { GeoPolygonFilter, isGeoPolygonFilter } from './geo_polygon_filter'; -import { - PhraseFilter, - isPhraseFilter, - isScriptedPhraseFilter, - getPhraseFilterField, - getPhraseFilterValue, -} from './phrase_filter'; -import { PhrasesFilter, isPhrasesFilter } from './phrases_filter'; -import { QueryStringFilter, isQueryStringFilter } from './query_string_filter'; -import { - RangeFilter, - isRangeFilter, - isScriptedRangeFilter, - RangeFilterParams, -} from './range_filter'; -import { MatchAllFilter, isMatchAllFilter } from './match_all_filter'; -import { MissingFilter, isMissingFilter } from './missing_filter'; - -export { - CustomFilter, - ExistsFilter, - isExistsFilter, - GeoBoundingBoxFilter, - isGeoBoundingBoxFilter, - GeoPolygonFilter, - isGeoPolygonFilter, - PhraseFilter, - isPhraseFilter, - isScriptedPhraseFilter, - getPhraseFilterField, - getPhraseFilterValue, - PhrasesFilter, - isPhrasesFilter, - QueryStringFilter, - isQueryStringFilter, - RangeFilter, - isRangeFilter, - isScriptedRangeFilter, - RangeFilterParams, - MatchAllFilter, - isMatchAllFilter, - MissingFilter, - isMissingFilter, -}; - -// Any filter associated with a field (used in the filter bar/editor) -export type FieldFilter = - | ExistsFilter - | GeoBoundingBoxFilter - | GeoPolygonFilter - | PhraseFilter - | PhrasesFilter - | RangeFilter - | MatchAllFilter - | MissingFilter; - -export enum FILTERS { - CUSTOM = 'custom', - PHRASES = 'phrases', - PHRASE = 'phrase', - EXISTS = 'exists', - MATCH_ALL = 'match_all', - MISSING = 'missing', - QUERY_STRING = 'query_string', - RANGE = 'range', - GEO_BOUNDING_BOX = 'geo_bounding_box', - GEO_POLYGON = 'geo_polygon', -} diff --git a/packages/kbn-es-query/src/filters/lib/phrase_filter.ts b/packages/kbn-es-query/src/filters/lib/phrase_filter.ts deleted file mode 100644 index 124a5da3724871..00000000000000 --- a/packages/kbn-es-query/src/filters/lib/phrase_filter.ts +++ /dev/null @@ -1,65 +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. - */ - -import { get, isPlainObject } from 'lodash'; -import { Filter, FilterMeta } from './meta_filter'; - -export type PhraseFilterMeta = FilterMeta & { - params: { - query: string; // The unformatted value - }; - script?: { - script: { - params: any; - }; - }; - field?: any; -}; - -export type PhraseFilter = Filter & { - meta: PhraseFilterMeta; -}; - -type PhraseFilterValue = string | number | boolean; - -export const isPhraseFilter = (filter: any): filter is PhraseFilter => { - const isMatchPhraseQuery = filter && filter.query && filter.query.match_phrase; - - const isDeprecatedMatchPhraseQuery = - filter && - filter.query && - filter.query.match && - Object.values(filter.query.match).find((params: any) => params.type === 'phrase'); - - return !!(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery); -}; - -export const isScriptedPhraseFilter = (filter: any): filter is PhraseFilter => - Boolean(get(filter, 'script.script.params.value')); - -export const getPhraseFilterField = (filter: PhraseFilter) => { - const queryConfig = filter.query.match_phrase || filter.query.match; - return Object.keys(queryConfig)[0]; -}; - -export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => { - const queryConfig = filter.query.match_phrase || filter.query.match; - const queryValue = Object.values(queryConfig)[0] as any; - return isPlainObject(queryValue) ? queryValue.query : queryValue; -}; diff --git a/packages/kbn-es-query/src/filters/lib/range_filter.ts b/packages/kbn-es-query/src/filters/lib/range_filter.ts deleted file mode 100644 index fc8d05d575d597..00000000000000 --- a/packages/kbn-es-query/src/filters/lib/range_filter.ts +++ /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. - */ -import { get, keys } from 'lodash'; -import { Filter, FilterMeta } from './meta_filter'; - -export interface RangeFilterParams { - from?: number | string; - to?: number | string; - gt?: number | string; - lt?: number | string; - gte?: number | string; - lte?: number | string; - format?: string; -} - -export type RangeFilterMeta = FilterMeta & { - params: RangeFilterParams; - field?: any; -}; - -export type RangeFilter = Filter & { - meta: RangeFilterMeta; - script?: { - script: { - params: any; - }; - }; - range: { [key: string]: RangeFilterParams }; -}; - -const hasRangeKeys = (params: RangeFilterParams) => - Boolean( - keys(params).find((key: string) => ['gte', 'gt', 'lte', 'lt', 'from', 'to'].includes(key)) - ); - -export const isRangeFilter = (filter: any): filter is RangeFilter => filter && filter.range; - -export const isScriptedRangeFilter = (filter: any): filter is RangeFilter => { - const params: RangeFilterParams = get(filter, 'script.script.params', {}); - - return hasRangeKeys(params); -}; diff --git a/packages/kbn-es-query/src/filters/phrase.js b/packages/kbn-es-query/src/filters/phrase.js deleted file mode 100644 index f0134f289ad9d2..00000000000000 --- a/packages/kbn-es-query/src/filters/phrase.js +++ /dev/null @@ -1,85 +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. - */ - -// Creates an filter where the given field matches the given value -export function buildPhraseFilter(field, value, indexPattern) { - const filter = { meta: { index: indexPattern.id } }; - const convertedValue = getConvertedValueForField(field, value); - - if (field.scripted) { - filter.script = getPhraseScript(field, value); - filter.meta.field = field.name; - } else { - filter.query = { match_phrase: {} }; - filter.query.match_phrase[field.name] = convertedValue; - } - return filter; -} - -export function getPhraseScript(field, value) { - const convertedValue = getConvertedValueForField(field, value); - const script = buildInlineScriptForPhraseFilter(field); - - return { - script: { - source: script, - lang: field.lang, - params: { - value: convertedValue - } - } - }; -} - -// See https://github.com/elastic/elasticsearch/issues/20941 and https://github.com/elastic/kibana/issues/8677 -// and https://github.com/elastic/elasticsearch/pull/22201 -// for the reason behind this change. Aggs now return boolean buckets with a key of 1 or 0. -export function getConvertedValueForField(field, value) { - if (typeof value !== 'boolean' && field.type === 'boolean') { - if ([1, 'true'].includes(value)) { - return true; - } - else if ([0, 'false'].includes(value)) { - return false; - } - else { - throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`); - } - } - return value; -} - -/** - * Takes a scripted field and returns an inline script appropriate for use in a script query. - * Handles lucene expression and Painless scripts. Other langs aren't guaranteed to generate valid - * scripts. - * - * @param {object} scriptedField A Field object representing a scripted field - * @returns {string} The inline script string - */ -export function buildInlineScriptForPhraseFilter(scriptedField) { - // We must wrap painless scripts in a lambda in case they're more than a simple expression - if (scriptedField.lang === 'painless') { - return `boolean compare(Supplier s, def v) {return s.get() == v;}` + - `compare(() -> { ${scriptedField.script} }, params.value);`; - } - else { - return `(${scriptedField.script}) == value`; - } -} diff --git a/packages/kbn-es-query/src/index.d.ts b/packages/kbn-es-query/src/index.d.ts index ca4455da33f45d..c06cef6367fe78 100644 --- a/packages/kbn-es-query/src/index.d.ts +++ b/packages/kbn-es-query/src/index.d.ts @@ -19,4 +19,3 @@ export * from './es_query'; export * from './kuery'; -export * from './filters'; diff --git a/packages/kbn-es-query/src/index.js b/packages/kbn-es-query/src/index.js index 086b2f6db8d0dc..963999bd0999b2 100644 --- a/packages/kbn-es-query/src/index.js +++ b/packages/kbn-es-query/src/index.js @@ -18,5 +18,4 @@ */ export * from './kuery'; -export * from './filters'; export * from './es_query'; diff --git a/packages/kbn-es-query/src/kuery/functions/is.js b/packages/kbn-es-query/src/kuery/functions/is.js index 33ae2112e3c0cb..63ade9e8793a7b 100644 --- a/packages/kbn-es-query/src/kuery/functions/is.js +++ b/packages/kbn-es-query/src/kuery/functions/is.js @@ -21,7 +21,7 @@ import _ from 'lodash'; import * as ast from '../ast'; import * as literal from '../node_types/literal'; import * as wildcard from '../node_types/wildcard'; -import { getPhraseScript } from '../../filters'; +import { getPhraseScript } from '../../utils/filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; diff --git a/packages/kbn-es-query/src/kuery/functions/range.js b/packages/kbn-es-query/src/kuery/functions/range.js index 80181cfc003f1c..f7719998ad5240 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.js +++ b/packages/kbn-es-query/src/kuery/functions/range.js @@ -20,7 +20,7 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { getRangeScript } from '../../filters'; +import { getRangeScript } from '../../utils/filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; diff --git a/packages/kbn-es-query/src/utils/filters.js b/packages/kbn-es-query/src/utils/filters.js new file mode 100644 index 00000000000000..6e4f5c342688ce --- /dev/null +++ b/packages/kbn-es-query/src/utils/filters.js @@ -0,0 +1,133 @@ +/* + * 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 { pick, get, reduce, map } from 'lodash'; + +/** @deprecated + * @see src/plugins/data/public/es_query/filters/phrase_filter.ts + * Code was already moved into src/plugins/data/public. + * This method will be removed after moving 'es_query' into new platform + * */ +export const getConvertedValueForField = (field, value) => { + if (typeof value !== 'boolean' && field.type === 'boolean') { + if ([1, 'true'].includes(value)) { + return true; + } else if ([0, 'false'].includes(value)) { + return false; + } else { + throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`); + } + } + return value; +}; + +/** @deprecated + * @see src/plugins/data/public/es_query/filters/phrase_filter.ts + * Code was already moved into src/plugins/data/public. + * This method will be removed after moving 'es_query' into new platform + * */ +export const buildInlineScriptForPhraseFilter = (scriptedField) => { + // We must wrap painless scripts in a lambda in case they're more than a simple expression + if (scriptedField.lang === 'painless') { + return ( + `boolean compare(Supplier s, def v) {return s.get() == v;}` + + `compare(() -> { ${scriptedField.script} }, params.value);` + ); + } else { + return `(${scriptedField.script}) == value`; + } +}; + +/** @deprecated + * @see src/plugins/data/public/es_query/filters/phrase_filter.ts + * Code was already moved into src/plugins/data/public. + * This method will be removed after moving 'es_query' into new platform + * */ +export function getPhraseScript(field, value) { + const convertedValue = getConvertedValueForField(field, value); + const script = buildInlineScriptForPhraseFilter(field); + + return { + script: { + source: script, + lang: field.lang, + params: { + value: convertedValue, + }, + }, + }; +} + +/** @deprecated + * @see src/plugins/data/public/es_query/filters/range_filter.ts + * Code was already moved into src/plugins/data/public. + * This method will be removed after moving 'kuery' into new platform + * */ +export function getRangeScript(field, params) { + const operators = { + gt: '>', + gte: '>=', + lte: '<=', + lt: '<', + }; + const comparators = { + gt: 'boolean gt(Supplier s, def v) {return s.get() > v}', + gte: 'boolean gte(Supplier s, def v) {return s.get() >= v}', + lte: 'boolean lte(Supplier s, def v) {return s.get() <= v}', + lt: 'boolean lt(Supplier s, def v) {return s.get() < v}', + }; + + const dateComparators = { + gt: 'boolean gt(Supplier s, def v) {return s.get().toInstant().isAfter(Instant.parse(v))}', + gte: 'boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))}', + lte: 'boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}', + lt: 'boolean lt(Supplier s, def v) {return s.get().toInstant().isBefore(Instant.parse(v))}', + }; + + const knownParams = pick(params, (val, key) => { + return key in operators; + }); + let script = map(knownParams, (val, key) => { + return '(' + field.script + ')' + get(operators, key) + key; + }).join(' && '); + + // We must wrap painless scripts in a lambda in case they're more than a simple expression + if (field.lang === 'painless') { + const comp = field.type === 'date' ? dateComparators : comparators; + const currentComparators = reduce( + knownParams, + (acc, val, key) => acc.concat(get(comp, key)), + [] + ).join(' '); + + const comparisons = map(knownParams, (val, key) => { + return `${key}(() -> { ${field.script} }, params.${key})`; + }).join(' && '); + + script = `${currentComparators}${comparisons}`; + } + + return { + script: { + source: script, + params: knownParams, + lang: field.lang, + }, + }; +} diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/index_patterns/index.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/index_patterns/index.js new file mode 100644 index 00000000000000..d15de7d98a9e0c --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/index_patterns/index.js @@ -0,0 +1 @@ +/* eslint-disable */ 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 f393a867d95e0d..577be820ccc7bc 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 @@ -172,6 +172,27 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, ], }, + + { + // Check if dirs that start with 'index' work correctly. + code: 'import { X } from "./index_patterns"', + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), + options: [ + { + basePath: __dirname, + zones: [ + { + target: ['files/no_restricted_paths/(public|server)/**/*'], + from: [ + 'files/no_restricted_paths/server/**/*', + '!files/no_restricted_paths/server/index.{ts,tsx}', + ], + allowSameFolder: true, + }, + ], + }, + ], + }, ], invalid: [ @@ -369,5 +390,34 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, ], }, + + { + // Don't use index*. + // It won't work with dirs that start with 'index'. + code: 'import { X } from "./index_patterns"', + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), + options: [ + { + basePath: __dirname, + zones: [ + { + target: ['files/no_restricted_paths/(public|server)/**/*'], + from: [ + 'files/no_restricted_paths/server/**/*', + '!files/no_restricted_paths/server/index*', + ], + allowSameFolder: true, + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "./index_patterns" imported in restricted zone.', + line: 1, + column: 19, + }, + ], + }, ], }); diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 2a8c22ed29a796..bbe12a93c241f7 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(483); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); @@ -152,7 +152,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(474); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(475); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(34); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -33210,7 +33210,7 @@ const WatchCommand = { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(275); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(374); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(377); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -33285,45 +33285,45 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Observable", function() { return _internal_Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]; }); -/* harmony import */ var _internal_observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _internal_observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(293); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ConnectableObservable", function() { return _internal_observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_1__["ConnectableObservable"]; }); -/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(299); +/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(298); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GroupedObservable", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_2__["GroupedObservable"]; }); -/* harmony import */ var _internal_symbol_observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(291); +/* harmony import */ var _internal_symbol_observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(290); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observable", function() { return _internal_symbol_observable__WEBPACK_IMPORTED_MODULE_3__["observable"]; }); -/* harmony import */ var _internal_Subject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(295); +/* harmony import */ var _internal_Subject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(294); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Subject", function() { return _internal_Subject__WEBPACK_IMPORTED_MODULE_4__["Subject"]; }); -/* harmony import */ var _internal_BehaviorSubject__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(300); +/* harmony import */ var _internal_BehaviorSubject__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(299); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "BehaviorSubject", function() { return _internal_BehaviorSubject__WEBPACK_IMPORTED_MODULE_5__["BehaviorSubject"]; }); -/* harmony import */ var _internal_ReplaySubject__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(301); +/* harmony import */ var _internal_ReplaySubject__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(300); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ReplaySubject", function() { return _internal_ReplaySubject__WEBPACK_IMPORTED_MODULE_6__["ReplaySubject"]; }); -/* harmony import */ var _internal_AsyncSubject__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(318); +/* harmony import */ var _internal_AsyncSubject__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(317); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "AsyncSubject", function() { return _internal_AsyncSubject__WEBPACK_IMPORTED_MODULE_7__["AsyncSubject"]; }); -/* harmony import */ var _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(319); +/* harmony import */ var _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(318); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asapScheduler", function() { return _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__["asap"]; }); -/* harmony import */ var _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(323); +/* harmony import */ var _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(322); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asyncScheduler", function() { return _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__["async"]; }); -/* harmony import */ var _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(302); +/* harmony import */ var _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(301); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "queueScheduler", function() { return _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__["queue"]; }); -/* harmony import */ var _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(324); +/* harmony import */ var _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(323); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "animationFrameScheduler", function() { return _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__["animationFrame"]; }); -/* harmony import */ var _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(327); +/* harmony import */ var _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(326); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "VirtualTimeScheduler", function() { return _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__["VirtualTimeScheduler"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "VirtualAction", function() { return _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__["VirtualAction"]; }); -/* harmony import */ var _internal_Scheduler__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(308); +/* harmony import */ var _internal_Scheduler__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(307); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Scheduler", function() { return _internal_Scheduler__WEBPACK_IMPORTED_MODULE_13__["Scheduler"]; }); /* harmony import */ var _internal_Subscription__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(284); @@ -33332,58 +33332,60 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_Subscriber__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(278); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Subscriber", function() { return _internal_Subscriber__WEBPACK_IMPORTED_MODULE_15__["Subscriber"]; }); -/* harmony import */ var _internal_Notification__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(310); +/* harmony import */ var _internal_Notification__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(309); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Notification", function() { return _internal_Notification__WEBPACK_IMPORTED_MODULE_16__["Notification"]; }); -/* harmony import */ var _internal_util_pipe__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(292); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "NotificationKind", function() { return _internal_Notification__WEBPACK_IMPORTED_MODULE_16__["NotificationKind"]; }); + +/* harmony import */ var _internal_util_pipe__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(291); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pipe", function() { return _internal_util_pipe__WEBPACK_IMPORTED_MODULE_17__["pipe"]; }); -/* harmony import */ var _internal_util_noop__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(293); +/* harmony import */ var _internal_util_noop__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(292); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "noop", function() { return _internal_util_noop__WEBPACK_IMPORTED_MODULE_18__["noop"]; }); -/* harmony import */ var _internal_util_identity__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(328); +/* harmony import */ var _internal_util_identity__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(327); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return _internal_util_identity__WEBPACK_IMPORTED_MODULE_19__["identity"]; }); -/* harmony import */ var _internal_util_isObservable__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(329); +/* harmony import */ var _internal_util_isObservable__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(328); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isObservable", function() { return _internal_util_isObservable__WEBPACK_IMPORTED_MODULE_20__["isObservable"]; }); -/* harmony import */ var _internal_util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(330); +/* harmony import */ var _internal_util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(329); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ArgumentOutOfRangeError", function() { return _internal_util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_21__["ArgumentOutOfRangeError"]; }); -/* harmony import */ var _internal_util_EmptyError__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(331); +/* harmony import */ var _internal_util_EmptyError__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(330); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "EmptyError", function() { return _internal_util_EmptyError__WEBPACK_IMPORTED_MODULE_22__["EmptyError"]; }); -/* harmony import */ var _internal_util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(296); +/* harmony import */ var _internal_util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(295); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ObjectUnsubscribedError", function() { return _internal_util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_23__["ObjectUnsubscribedError"]; }); -/* harmony import */ var _internal_util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(289); +/* harmony import */ var _internal_util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(287); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "UnsubscriptionError", function() { return _internal_util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_24__["UnsubscriptionError"]; }); -/* harmony import */ var _internal_util_TimeoutError__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(332); +/* harmony import */ var _internal_util_TimeoutError__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(331); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TimeoutError", function() { return _internal_util_TimeoutError__WEBPACK_IMPORTED_MODULE_25__["TimeoutError"]; }); -/* harmony import */ var _internal_observable_bindCallback__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(333); +/* harmony import */ var _internal_observable_bindCallback__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(332); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bindCallback", function() { return _internal_observable_bindCallback__WEBPACK_IMPORTED_MODULE_26__["bindCallback"]; }); -/* harmony import */ var _internal_observable_bindNodeCallback__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(335); +/* harmony import */ var _internal_observable_bindNodeCallback__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(334); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bindNodeCallback", function() { return _internal_observable_bindNodeCallback__WEBPACK_IMPORTED_MODULE_27__["bindNodeCallback"]; }); -/* harmony import */ var _internal_observable_combineLatest__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(336); +/* harmony import */ var _internal_observable_combineLatest__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(335); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_observable_combineLatest__WEBPACK_IMPORTED_MODULE_28__["combineLatest"]; }); -/* harmony import */ var _internal_observable_concat__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(347); +/* harmony import */ var _internal_observable_concat__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(346); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_observable_concat__WEBPACK_IMPORTED_MODULE_29__["concat"]; }); /* harmony import */ var _internal_observable_defer__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(357); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return _internal_observable_defer__WEBPACK_IMPORTED_MODULE_30__["defer"]; }); -/* harmony import */ var _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(311); +/* harmony import */ var _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(310); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "empty", function() { return _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__["empty"]; }); /* harmony import */ var _internal_observable_forkJoin__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(358); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "forkJoin", function() { return _internal_observable_forkJoin__WEBPACK_IMPORTED_MODULE_32__["forkJoin"]; }); -/* harmony import */ var _internal_observable_from__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(348); +/* harmony import */ var _internal_observable_from__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(350); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "from", function() { return _internal_observable_from__WEBPACK_IMPORTED_MODULE_33__["from"]; }); /* harmony import */ var _internal_observable_fromEvent__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(359); @@ -33407,7 +33409,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(366); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "never", function() { return _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__["never"]; }); -/* harmony import */ var _internal_observable_of__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(312); +/* harmony import */ var _internal_observable_of__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(311); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "of", function() { return _internal_observable_of__WEBPACK_IMPORTED_MODULE_41__["of"]; }); /* harmony import */ var _internal_observable_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(367); @@ -33416,30 +33418,36 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_observable_pairs__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(368); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairs", function() { return _internal_observable_pairs__WEBPACK_IMPORTED_MODULE_43__["pairs"]; }); -/* harmony import */ var _internal_observable_race__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(369); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_observable_race__WEBPACK_IMPORTED_MODULE_44__["race"]; }); +/* harmony import */ var _internal_observable_partition__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(369); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_observable_partition__WEBPACK_IMPORTED_MODULE_44__["partition"]; }); + +/* harmony import */ var _internal_observable_race__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(372); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_observable_race__WEBPACK_IMPORTED_MODULE_45__["race"]; }); + +/* harmony import */ var _internal_observable_range__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(373); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "range", function() { return _internal_observable_range__WEBPACK_IMPORTED_MODULE_46__["range"]; }); -/* harmony import */ var _internal_observable_range__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(370); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "range", function() { return _internal_observable_range__WEBPACK_IMPORTED_MODULE_45__["range"]; }); +/* harmony import */ var _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(316); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwError", function() { return _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_47__["throwError"]; }); -/* harmony import */ var _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(317); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwError", function() { return _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_46__["throwError"]; }); +/* harmony import */ var _internal_observable_timer__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(374); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timer", function() { return _internal_observable_timer__WEBPACK_IMPORTED_MODULE_48__["timer"]; }); -/* harmony import */ var _internal_observable_timer__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(371); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timer", function() { return _internal_observable_timer__WEBPACK_IMPORTED_MODULE_47__["timer"]; }); +/* harmony import */ var _internal_observable_using__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(375); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "using", function() { return _internal_observable_using__WEBPACK_IMPORTED_MODULE_49__["using"]; }); -/* harmony import */ var _internal_observable_using__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(372); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "using", function() { return _internal_observable_using__WEBPACK_IMPORTED_MODULE_48__["using"]; }); +/* harmony import */ var _internal_observable_zip__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(376); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_observable_zip__WEBPACK_IMPORTED_MODULE_50__["zip"]; }); -/* harmony import */ var _internal_observable_zip__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(373); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_observable_zip__WEBPACK_IMPORTED_MODULE_49__["zip"]; }); +/* harmony import */ var _internal_scheduled_scheduled__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(351); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scheduled", function() { return _internal_scheduled_scheduled__WEBPACK_IMPORTED_MODULE_51__["scheduled"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "EMPTY", function() { return _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__["EMPTY"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "NEVER", function() { return _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__["NEVER"]; }); -/* harmony import */ var _internal_config__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(282); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "config", function() { return _internal_config__WEBPACK_IMPORTED_MODULE_50__["config"]; }); +/* harmony import */ var _internal_config__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(282); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "config", function() { return _internal_config__WEBPACK_IMPORTED_MODULE_52__["config"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -33492,6 +33500,8 @@ __webpack_require__.r(__webpack_exports__); + + @@ -33505,11 +33515,13 @@ __webpack_require__.r(__webpack_exports__); "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Observable", function() { return Observable; }); -/* harmony import */ var _util_toSubscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(277); -/* harmony import */ var _internal_symbol_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(291); -/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(292); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(282); -/** PURE_IMPORTS_START _util_toSubscriber,_internal_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ +/* harmony import */ var _util_canReportError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(277); +/* harmony import */ var _util_toSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(289); +/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(290); +/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(291); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(282); +/** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ + @@ -33529,16 +33541,16 @@ var Observable = /*@__PURE__*/ (function () { }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { var operator = this.operator; - var sink = Object(_util_toSubscriber__WEBPACK_IMPORTED_MODULE_0__["toSubscriber"])(observerOrNext, error, complete); + var sink = Object(_util_toSubscriber__WEBPACK_IMPORTED_MODULE_1__["toSubscriber"])(observerOrNext, error, complete); if (operator) { - operator.call(sink, this.source); + sink.add(operator.call(sink, this.source)); } else { - sink.add(this.source || (_config__WEBPACK_IMPORTED_MODULE_3__["config"].useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? + sink.add(this.source || (_config__WEBPACK_IMPORTED_MODULE_4__["config"].useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? this._subscribe(sink) : this._trySubscribe(sink)); } - if (_config__WEBPACK_IMPORTED_MODULE_3__["config"].useDeprecatedSynchronousErrorHandling) { + if (_config__WEBPACK_IMPORTED_MODULE_4__["config"].useDeprecatedSynchronousErrorHandling) { if (sink.syncErrorThrowable) { sink.syncErrorThrowable = false; if (sink.syncErrorThrown) { @@ -33553,11 +33565,16 @@ var Observable = /*@__PURE__*/ (function () { return this._subscribe(sink); } catch (err) { - if (_config__WEBPACK_IMPORTED_MODULE_3__["config"].useDeprecatedSynchronousErrorHandling) { + if (_config__WEBPACK_IMPORTED_MODULE_4__["config"].useDeprecatedSynchronousErrorHandling) { sink.syncErrorThrown = true; sink.syncErrorValue = err; } - sink.error(err); + if (Object(_util_canReportError__WEBPACK_IMPORTED_MODULE_0__["canReportError"])(sink)) { + sink.error(err); + } + else { + console.warn(err); + } } }; Observable.prototype.forEach = function (next, promiseCtor) { @@ -33582,7 +33599,7 @@ var Observable = /*@__PURE__*/ (function () { var source = this.source; return source && source.subscribe(subscriber); }; - Observable.prototype[_internal_symbol_observable__WEBPACK_IMPORTED_MODULE_1__["observable"]] = function () { + Observable.prototype[_symbol_observable__WEBPACK_IMPORTED_MODULE_2__["observable"]] = function () { return this; }; Observable.prototype.pipe = function () { @@ -33593,7 +33610,7 @@ var Observable = /*@__PURE__*/ (function () { if (operations.length === 0) { return this; } - return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_2__["pipeFromArray"])(operations)(this); + return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipeFromArray"])(operations)(this); }; Observable.prototype.toPromise = function (promiseCtor) { var _this = this; @@ -33611,7 +33628,7 @@ var Observable = /*@__PURE__*/ (function () { function getPromiseCtor(promiseCtor) { if (!promiseCtor) { - promiseCtor = _config__WEBPACK_IMPORTED_MODULE_3__["config"].Promise || Promise; + promiseCtor = _config__WEBPACK_IMPORTED_MODULE_4__["config"].Promise || Promise; } if (!promiseCtor) { throw new Error('no Promise impl found'); @@ -33627,29 +33644,26 @@ function getPromiseCtor(promiseCtor) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toSubscriber", function() { return toSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "canReportError", function() { return canReportError; }); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(278); -/* harmony import */ var _symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(290); -/* harmony import */ var _Observer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(281); -/** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */ - +/** PURE_IMPORTS_START _Subscriber PURE_IMPORTS_END */ - -function toSubscriber(nextOrObserver, error, complete) { - if (nextOrObserver) { - if (nextOrObserver instanceof _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"]) { - return nextOrObserver; +function canReportError(observer) { + while (observer) { + var _a = observer, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped; + if (closed_1 || isStopped) { + return false; } - if (nextOrObserver[_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__["rxSubscriber"]]) { - return nextOrObserver[_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__["rxSubscriber"]](); + else if (destination && destination instanceof _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"]) { + observer = destination; + } + else { + observer = null; } } - if (!nextOrObserver && !error && !complete) { - return new _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"](_Observer__WEBPACK_IMPORTED_MODULE_2__["empty"]); - } - return new _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"](nextOrObserver, error, complete); + return true; } -//# sourceMappingURL=toSubscriber.js.map +//# sourceMappingURL=canReportError.js.map /***/ }), @@ -33659,11 +33673,12 @@ function toSubscriber(nextOrObserver, error, complete) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Subscriber", function() { return Subscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SafeSubscriber", function() { return SafeSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); /* harmony import */ var _Observer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(281); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(284); -/* harmony import */ var _internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(290); +/* harmony import */ var _internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(288); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(282); /* harmony import */ var _util_hostReportError__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(283); /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ @@ -33692,11 +33707,10 @@ var Subscriber = /*@__PURE__*/ (function (_super) { break; } if (typeof destinationOrNext === 'object') { - if (isTrustedSubscriber(destinationOrNext)) { - var trustedSubscriber = destinationOrNext[_internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_4__["rxSubscriber"]](); - _this.syncErrorThrowable = trustedSubscriber.syncErrorThrowable; - _this.destination = trustedSubscriber; - trustedSubscriber.add(_this); + if (destinationOrNext instanceof Subscriber) { + _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; + _this.destination = destinationOrNext; + destinationOrNext.add(_this); } else { _this.syncErrorThrowable = true; @@ -33753,14 +33767,12 @@ var Subscriber = /*@__PURE__*/ (function (_super) { this.unsubscribe(); }; Subscriber.prototype._unsubscribeAndRecycle = function () { - var _a = this, _parent = _a._parent, _parents = _a._parents; - this._parent = null; - this._parents = null; + var _parentOrParents = this._parentOrParents; + this._parentOrParents = null; this.unsubscribe(); this.closed = false; this.isStopped = false; - this._parent = _parent; - this._parents = _parents; + this._parentOrParents = _parentOrParents; return this; }; return Subscriber; @@ -33900,9 +33912,7 @@ var SafeSubscriber = /*@__PURE__*/ (function (_super) { }; return SafeSubscriber; }(Subscriber)); -function isTrustedSubscriber(obj) { - return obj instanceof Subscriber || ('syncErrorThrowable' in obj && obj[_internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_4__["rxSubscriber"]]); -} + //# sourceMappingURL=Subscriber.js.map @@ -34198,7 +34208,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hostReportError", function() { return hostReportError; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ function hostReportError(err) { - setTimeout(function () { throw err; }); + setTimeout(function () { throw err; }, 0); } //# sourceMappingURL=hostReportError.js.map @@ -34213,12 +34223,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(285); /* harmony import */ var _util_isObject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(286); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(280); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(288); -/* harmony import */ var _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(289); -/** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_tryCatch,_util_errorObject,_util_UnsubscriptionError PURE_IMPORTS_END */ - - +/* harmony import */ var _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(287); +/** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_UnsubscriptionError PURE_IMPORTS_END */ @@ -34226,94 +34232,112 @@ __webpack_require__.r(__webpack_exports__); var Subscription = /*@__PURE__*/ (function () { function Subscription(unsubscribe) { this.closed = false; - this._parent = null; - this._parents = null; + this._parentOrParents = null; this._subscriptions = null; if (unsubscribe) { this._unsubscribe = unsubscribe; } } Subscription.prototype.unsubscribe = function () { - var hasErrors = false; var errors; if (this.closed) { return; } - var _a = this, _parent = _a._parent, _parents = _a._parents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; + var _a = this, _parentOrParents = _a._parentOrParents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; this.closed = true; - this._parent = null; - this._parents = null; + this._parentOrParents = null; this._subscriptions = null; - var index = -1; - var len = _parents ? _parents.length : 0; - while (_parent) { - _parent.remove(this); - _parent = ++index < len && _parents[index] || null; + if (_parentOrParents instanceof Subscription) { + _parentOrParents.remove(this); + } + else if (_parentOrParents !== null) { + for (var index = 0; index < _parentOrParents.length; ++index) { + var parent_1 = _parentOrParents[index]; + parent_1.remove(this); + } } if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_2__["isFunction"])(_unsubscribe)) { - var trial = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_3__["tryCatch"])(_unsubscribe).call(this); - if (trial === _util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"]) { - hasErrors = true; - errors = errors || (_util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"].e instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_5__["UnsubscriptionError"] ? - flattenUnsubscriptionErrors(_util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"].e.errors) : [_util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"].e]); + try { + _unsubscribe.call(this); + } + catch (e) { + errors = e instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_3__["UnsubscriptionError"] ? flattenUnsubscriptionErrors(e.errors) : [e]; } } if (Object(_util_isArray__WEBPACK_IMPORTED_MODULE_0__["isArray"])(_subscriptions)) { - index = -1; - len = _subscriptions.length; + var index = -1; + var len = _subscriptions.length; while (++index < len) { var sub = _subscriptions[index]; if (Object(_util_isObject__WEBPACK_IMPORTED_MODULE_1__["isObject"])(sub)) { - var trial = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_3__["tryCatch"])(sub.unsubscribe).call(sub); - if (trial === _util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"]) { - hasErrors = true; + try { + sub.unsubscribe(); + } + catch (e) { errors = errors || []; - var err = _util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"].e; - if (err instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_5__["UnsubscriptionError"]) { - errors = errors.concat(flattenUnsubscriptionErrors(err.errors)); + if (e instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_3__["UnsubscriptionError"]) { + errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); } else { - errors.push(err); + errors.push(e); } } } } } - if (hasErrors) { - throw new _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_5__["UnsubscriptionError"](errors); + if (errors) { + throw new _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_3__["UnsubscriptionError"](errors); } }; Subscription.prototype.add = function (teardown) { - if (!teardown || (teardown === Subscription.EMPTY)) { + var subscription = teardown; + if (!teardown) { return Subscription.EMPTY; } - if (teardown === this) { - return this; - } - var subscription = teardown; switch (typeof teardown) { case 'function': subscription = new Subscription(teardown); case 'object': - if (subscription.closed || typeof subscription.unsubscribe !== 'function') { + if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') { return subscription; } else if (this.closed) { subscription.unsubscribe(); return subscription; } - else if (typeof subscription._addParent !== 'function') { + else if (!(subscription instanceof Subscription)) { var tmp = subscription; subscription = new Subscription(); subscription._subscriptions = [tmp]; } break; - default: + default: { throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); + } + } + var _parentOrParents = subscription._parentOrParents; + if (_parentOrParents === null) { + subscription._parentOrParents = this; + } + else if (_parentOrParents instanceof Subscription) { + if (_parentOrParents === this) { + return subscription; + } + subscription._parentOrParents = [_parentOrParents, this]; + } + else if (_parentOrParents.indexOf(this) === -1) { + _parentOrParents.push(this); + } + else { + return subscription; + } + var subscriptions = this._subscriptions; + if (subscriptions === null) { + this._subscriptions = [subscription]; + } + else { + subscriptions.push(subscription); } - var subscriptions = this._subscriptions || (this._subscriptions = []); - subscriptions.push(subscription); - subscription._addParent(this); return subscription; }; Subscription.prototype.remove = function (subscription) { @@ -34325,18 +34349,6 @@ var Subscription = /*@__PURE__*/ (function () { } } }; - Subscription.prototype._addParent = function (parent) { - var _a = this, _parent = _a._parent, _parents = _a._parents; - if (!_parent || _parent === parent) { - this._parent = parent; - } - else if (!_parents) { - this._parents = [parent]; - } - else if (_parents.indexOf(parent) === -1) { - _parents.push(parent); - } - }; Subscription.EMPTY = (function (empty) { empty.closed = true; return empty; @@ -34345,7 +34357,7 @@ var Subscription = /*@__PURE__*/ (function () { }()); function flattenUnsubscriptionErrors(errors) { - return errors.reduce(function (errs, err) { return errs.concat((err instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_5__["UnsubscriptionError"]) ? err.errors : err); }, []); + return errors.reduce(function (errs, err) { return errs.concat((err instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_3__["UnsubscriptionError"]) ? err.errors : err); }, []); } //# sourceMappingURL=Subscription.js.map @@ -34358,7 +34370,7 @@ function flattenUnsubscriptionErrors(errors) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isArray", function() { return isArray; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ -var isArray = Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); +var isArray = /*@__PURE__*/ (function () { return Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); })(); //# sourceMappingURL=isArray.js.map @@ -34371,7 +34383,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isObject", function() { return isObject; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ function isObject(x) { - return x != null && typeof x === 'object'; + return x !== null && typeof x === 'object'; } //# sourceMappingURL=isObject.js.map @@ -34382,25 +34394,22 @@ function isObject(x) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "tryCatch", function() { return tryCatch; }); -/* harmony import */ var _errorObject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(288); -/** PURE_IMPORTS_START _errorObject PURE_IMPORTS_END */ - -var tryCatchTarget; -function tryCatcher() { - try { - return tryCatchTarget.apply(this, arguments); - } - catch (e) { - _errorObject__WEBPACK_IMPORTED_MODULE_0__["errorObject"].e = e; - return _errorObject__WEBPACK_IMPORTED_MODULE_0__["errorObject"]; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UnsubscriptionError", function() { return UnsubscriptionError; }); +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +var UnsubscriptionErrorImpl = /*@__PURE__*/ (function () { + function UnsubscriptionErrorImpl(errors) { + Error.call(this); + this.message = errors ? + errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : ''; + this.name = 'UnsubscriptionError'; + this.errors = errors; + return this; } -} -function tryCatch(fn) { - tryCatchTarget = fn; - return tryCatcher; -} -//# sourceMappingURL=tryCatch.js.map + UnsubscriptionErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return UnsubscriptionErrorImpl; +})(); +var UnsubscriptionError = UnsubscriptionErrorImpl; +//# sourceMappingURL=UnsubscriptionError.js.map /***/ }), @@ -34409,10 +34418,16 @@ function tryCatch(fn) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "errorObject", function() { return errorObject; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rxSubscriber", function() { return rxSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$$rxSubscriber", function() { return $$rxSubscriber; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ -var errorObject = { e: {} }; -//# sourceMappingURL=errorObject.js.map +var rxSubscriber = /*@__PURE__*/ (function () { + return typeof Symbol === 'function' + ? /*@__PURE__*/ Symbol('rxSubscriber') + : '@@rxSubscriber_' + /*@__PURE__*/ Math.random(); +})(); +var $$rxSubscriber = rxSubscriber; +//# sourceMappingURL=rxSubscriber.js.map /***/ }), @@ -34421,63 +34436,52 @@ var errorObject = { e: {} }; "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UnsubscriptionError", function() { return UnsubscriptionError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/** PURE_IMPORTS_START tslib PURE_IMPORTS_END */ - -var UnsubscriptionError = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](UnsubscriptionError, _super); - function UnsubscriptionError(errors) { - var _this = _super.call(this, errors ? - errors.length + " errors occurred during unsubscription:\n " + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : '') || this; - _this.errors = errors; - _this.name = 'UnsubscriptionError'; - Object.setPrototypeOf(_this, UnsubscriptionError.prototype); - return _this; - } - return UnsubscriptionError; -}(Error)); - -//# sourceMappingURL=UnsubscriptionError.js.map +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toSubscriber", function() { return toSubscriber; }); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(278); +/* harmony import */ var _symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(288); +/* harmony import */ var _Observer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(281); +/** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */ -/***/ }), -/* 290 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rxSubscriber", function() { return rxSubscriber; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$$rxSubscriber", function() { return $$rxSubscriber; }); -/** PURE_IMPORTS_START PURE_IMPORTS_END */ -var rxSubscriber = (typeof Symbol === 'function' && typeof Symbol.for === 'function') - ? /*@__PURE__*/ Symbol.for('rxSubscriber') - : '@@rxSubscriber'; -var $$rxSubscriber = rxSubscriber; -//# sourceMappingURL=rxSubscriber.js.map +function toSubscriber(nextOrObserver, error, complete) { + if (nextOrObserver) { + if (nextOrObserver instanceof _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"]) { + return nextOrObserver; + } + if (nextOrObserver[_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__["rxSubscriber"]]) { + return nextOrObserver[_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_1__["rxSubscriber"]](); + } + } + if (!nextOrObserver && !error && !complete) { + return new _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"](_Observer__WEBPACK_IMPORTED_MODULE_2__["empty"]); + } + return new _Subscriber__WEBPACK_IMPORTED_MODULE_0__["Subscriber"](nextOrObserver, error, complete); +} +//# sourceMappingURL=toSubscriber.js.map /***/ }), -/* 291 */ +/* 290 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "observable", function() { return observable; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ -var observable = typeof Symbol === 'function' && Symbol.observable || '@@observable'; +var observable = /*@__PURE__*/ (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })(); //# sourceMappingURL=observable.js.map /***/ }), -/* 292 */ +/* 291 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pipe", function() { return pipe; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pipeFromArray", function() { return pipeFromArray; }); -/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(293); +/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292); /** PURE_IMPORTS_START _noop PURE_IMPORTS_END */ function pipe() { @@ -34502,7 +34506,7 @@ function pipeFromArray(fns) { /***/ }), -/* 293 */ +/* 292 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34514,7 +34518,7 @@ function noop() { } /***/ }), -/* 294 */ +/* 293 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34522,11 +34526,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ConnectableObservable", function() { return ConnectableObservable; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "connectableObservableDescriptor", function() { return connectableObservableDescriptor; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(276); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(278); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(284); -/* harmony import */ var _operators_refCount__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(298); +/* harmony import */ var _operators_refCount__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(297); /** PURE_IMPORTS_START tslib,_Subject,_Observable,_Subscriber,_Subscription,_operators_refCount PURE_IMPORTS_END */ @@ -34565,9 +34569,6 @@ var ConnectableObservable = /*@__PURE__*/ (function (_super) { this._connection = null; connection = _Subscription__WEBPACK_IMPORTED_MODULE_4__["Subscription"].EMPTY; } - else { - this._connection = connection; - } } return connection; }; @@ -34577,18 +34578,20 @@ var ConnectableObservable = /*@__PURE__*/ (function (_super) { return ConnectableObservable; }(_Observable__WEBPACK_IMPORTED_MODULE_2__["Observable"])); -var connectableProto = ConnectableObservable.prototype; -var connectableObservableDescriptor = { - operator: { value: null }, - _refCount: { value: 0, writable: true }, - _subject: { value: null, writable: true }, - _connection: { value: null, writable: true }, - _subscribe: { value: connectableProto._subscribe }, - _isComplete: { value: connectableProto._isComplete, writable: true }, - getSubject: { value: connectableProto.getSubject }, - connect: { value: connectableProto.connect }, - refCount: { value: connectableProto.refCount } -}; +var connectableObservableDescriptor = /*@__PURE__*/ (function () { + var connectableProto = ConnectableObservable.prototype; + return { + operator: { value: null }, + _refCount: { value: 0, writable: true }, + _subject: { value: null, writable: true }, + _connection: { value: null, writable: true }, + _subscribe: { value: connectableProto._subscribe }, + _isComplete: { value: connectableProto._isComplete, writable: true }, + getSubject: { value: connectableProto.getSubject }, + connect: { value: connectableProto.connect }, + refCount: { value: connectableProto.refCount } + }; +})(); var ConnectableSubscriber = /*@__PURE__*/ (function (_super) { tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ConnectableSubscriber, _super); function ConnectableSubscriber(destination, connectable) { @@ -34673,7 +34676,7 @@ var RefCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 295 */ +/* 294 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34685,9 +34688,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(276); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(278); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(284); -/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(296); -/* harmony import */ var _SubjectSubscription__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(297); -/* harmony import */ var _internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(290); +/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(295); +/* harmony import */ var _SubjectSubscription__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(296); +/* harmony import */ var _internal_symbol_rxSubscriber__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(288); /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ @@ -34849,31 +34852,29 @@ var AnonymousSubject = /*@__PURE__*/ (function (_super) { /***/ }), -/* 296 */ +/* 295 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ObjectUnsubscribedError", function() { return ObjectUnsubscribedError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/** PURE_IMPORTS_START tslib PURE_IMPORTS_END */ - -var ObjectUnsubscribedError = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ObjectUnsubscribedError, _super); - function ObjectUnsubscribedError() { - var _this = _super.call(this, 'object unsubscribed') || this; - _this.name = 'ObjectUnsubscribedError'; - Object.setPrototypeOf(_this, ObjectUnsubscribedError.prototype); - return _this; +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +var ObjectUnsubscribedErrorImpl = /*@__PURE__*/ (function () { + function ObjectUnsubscribedErrorImpl() { + Error.call(this); + this.message = 'object unsubscribed'; + this.name = 'ObjectUnsubscribedError'; + return this; } - return ObjectUnsubscribedError; -}(Error)); - + ObjectUnsubscribedErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return ObjectUnsubscribedErrorImpl; +})(); +var ObjectUnsubscribedError = ObjectUnsubscribedErrorImpl; //# sourceMappingURL=ObjectUnsubscribedError.js.map /***/ }), -/* 297 */ +/* 296 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34916,7 +34917,7 @@ var SubjectSubscription = /*@__PURE__*/ (function (_super) { /***/ }), -/* 298 */ +/* 297 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34985,7 +34986,7 @@ var RefCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 299 */ +/* 298 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -34996,7 +34997,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(284); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(276); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(295); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(294); /** PURE_IMPORTS_START tslib,_Subscriber,_Subscription,_Observable,_Subject PURE_IMPORTS_END */ @@ -35182,15 +35183,15 @@ var InnerRefCountSubscription = /*@__PURE__*/ (function (_super) { /***/ }), -/* 300 */ +/* 299 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BehaviorSubject", function() { return BehaviorSubject; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(296); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(295); /** PURE_IMPORTS_START tslib,_Subject,_util_ObjectUnsubscribedError PURE_IMPORTS_END */ @@ -35237,19 +35238,19 @@ var BehaviorSubject = /*@__PURE__*/ (function (_super) { /***/ }), -/* 301 */ +/* 300 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReplaySubject", function() { return ReplaySubject; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _scheduler_queue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(302); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _scheduler_queue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(301); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(284); -/* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(309); -/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(296); -/* harmony import */ var _SubjectSubscription__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(297); +/* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(308); +/* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(295); +/* harmony import */ var _SubjectSubscription__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(296); /** PURE_IMPORTS_START tslib,_Subject,_scheduler_queue,_Subscription,_operators_observeOn,_util_ObjectUnsubscribedError,_SubjectSubscription PURE_IMPORTS_END */ @@ -35370,14 +35371,14 @@ var ReplayEvent = /*@__PURE__*/ (function () { /***/ }), -/* 302 */ +/* 301 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "queue", function() { return queue; }); -/* harmony import */ var _QueueAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(303); -/* harmony import */ var _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(306); +/* harmony import */ var _QueueAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(302); +/* harmony import */ var _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(305); /** PURE_IMPORTS_START _QueueAction,_QueueScheduler PURE_IMPORTS_END */ @@ -35386,14 +35387,14 @@ var queue = /*@__PURE__*/ new _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__["Queu /***/ }), -/* 303 */ +/* 302 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "QueueAction", function() { return QueueAction; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(304); +/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(303); /** PURE_IMPORTS_START tslib,_AsyncAction PURE_IMPORTS_END */ @@ -35438,14 +35439,14 @@ var QueueAction = /*@__PURE__*/ (function (_super) { /***/ }), -/* 304 */ +/* 303 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsyncAction", function() { return AsyncAction; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Action__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(305); +/* harmony import */ var _Action__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(304); /** PURE_IMPORTS_START tslib,_Action PURE_IMPORTS_END */ @@ -35489,7 +35490,8 @@ var AsyncAction = /*@__PURE__*/ (function (_super) { if (delay !== null && this.delay === delay && this.pending === false) { return id; } - return clearInterval(id) && undefined || undefined; + clearInterval(id); + return undefined; }; AsyncAction.prototype.execute = function (state, delay) { if (this.closed) { @@ -35543,7 +35545,7 @@ var AsyncAction = /*@__PURE__*/ (function (_super) { /***/ }), -/* 305 */ +/* 304 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -35572,14 +35574,14 @@ var Action = /*@__PURE__*/ (function (_super) { /***/ }), -/* 306 */ +/* 305 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "QueueScheduler", function() { return QueueScheduler; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(307); +/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(306); /** PURE_IMPORTS_START tslib,_AsyncScheduler PURE_IMPORTS_END */ @@ -35595,14 +35597,14 @@ var QueueScheduler = /*@__PURE__*/ (function (_super) { /***/ }), -/* 307 */ +/* 306 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsyncScheduler", function() { return AsyncScheduler; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Scheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(308); +/* harmony import */ var _Scheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(307); /** PURE_IMPORTS_START tslib,_Scheduler PURE_IMPORTS_END */ @@ -35664,7 +35666,7 @@ var AsyncScheduler = /*@__PURE__*/ (function (_super) { /***/ }), -/* 308 */ +/* 307 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -35684,7 +35686,7 @@ var Scheduler = /*@__PURE__*/ (function () { } return new this.SchedulerAction(this, work).schedule(state, delay); }; - Scheduler.now = Date.now ? Date.now : function () { return +new Date(); }; + Scheduler.now = function () { return Date.now(); }; return Scheduler; }()); @@ -35692,7 +35694,7 @@ var Scheduler = /*@__PURE__*/ (function () { /***/ }), -/* 309 */ +/* 308 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -35703,7 +35705,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ObserveOnMessage", function() { return ObserveOnMessage; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(310); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(309); /** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -35747,16 +35749,19 @@ var ObserveOnSubscriber = /*@__PURE__*/ (function (_super) { this.unsubscribe(); }; ObserveOnSubscriber.prototype.scheduleMessage = function (notification) { - this.add(this.scheduler.schedule(ObserveOnSubscriber.dispatch, this.delay, new ObserveOnMessage(notification, this.destination))); + var destination = this.destination; + destination.add(this.scheduler.schedule(ObserveOnSubscriber.dispatch, this.delay, new ObserveOnMessage(notification, this.destination))); }; ObserveOnSubscriber.prototype._next = function (value) { this.scheduleMessage(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createNext(value)); }; ObserveOnSubscriber.prototype._error = function (err) { this.scheduleMessage(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createError(err)); + this.unsubscribe(); }; ObserveOnSubscriber.prototype._complete = function () { this.scheduleMessage(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createComplete()); + this.unsubscribe(); }; return ObserveOnSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); @@ -35773,19 +35778,26 @@ var ObserveOnMessage = /*@__PURE__*/ (function () { /***/ }), -/* 310 */ +/* 309 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NotificationKind", function() { return NotificationKind; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Notification", function() { return Notification; }); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(311); -/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(312); -/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(317); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(310); +/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(311); +/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(316); /** PURE_IMPORTS_START _observable_empty,_observable_of,_observable_throwError PURE_IMPORTS_END */ +var NotificationKind; +/*@__PURE__*/ (function (NotificationKind) { + NotificationKind["NEXT"] = "N"; + NotificationKind["ERROR"] = "E"; + NotificationKind["COMPLETE"] = "C"; +})(NotificationKind || (NotificationKind = {})); var Notification = /*@__PURE__*/ (function () { function Notification(kind, value, error) { this.kind = kind; @@ -35855,14 +35867,13 @@ var Notification = /*@__PURE__*/ (function () { /***/ }), -/* 311 */ +/* 310 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EMPTY", function() { return EMPTY; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "empty", function() { return empty; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "emptyScheduled", function() { return emptyScheduled; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ @@ -35877,18 +35888,16 @@ function emptyScheduled(scheduler) { /***/ }), -/* 312 */ +/* 311 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "of", function() { return of; }); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(313); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); -/* harmony import */ var _scalar__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(316); -/** PURE_IMPORTS_START _util_isScheduler,_fromArray,_empty,_scalar PURE_IMPORTS_END */ - +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(312); +/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(313); +/* harmony import */ var _scheduled_scheduleArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(315); +/** PURE_IMPORTS_START _util_isScheduler,_fromArray,_scheduled_scheduleArray PURE_IMPORTS_END */ @@ -35900,24 +35909,17 @@ function of() { var scheduler = args[args.length - 1]; if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_0__["isScheduler"])(scheduler)) { args.pop(); + return Object(_scheduled_scheduleArray__WEBPACK_IMPORTED_MODULE_2__["scheduleArray"])(args, scheduler); } else { - scheduler = undefined; - } - switch (args.length) { - case 0: - return Object(_empty__WEBPACK_IMPORTED_MODULE_2__["empty"])(scheduler); - case 1: - return scheduler ? Object(_fromArray__WEBPACK_IMPORTED_MODULE_1__["fromArray"])(args, scheduler) : Object(_scalar__WEBPACK_IMPORTED_MODULE_3__["scalar"])(args[0]); - default: - return Object(_fromArray__WEBPACK_IMPORTED_MODULE_1__["fromArray"])(args, scheduler); + return Object(_fromArray__WEBPACK_IMPORTED_MODULE_1__["fromArray"])(args); } } //# sourceMappingURL=of.js.map /***/ }), -/* 313 */ +/* 312 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -35931,46 +35933,32 @@ function isScheduler(value) { /***/ }), -/* 314 */ +/* 313 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromArray", function() { return fromArray; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _util_subscribeToArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(315); -/** PURE_IMPORTS_START _Observable,_Subscription,_util_subscribeToArray PURE_IMPORTS_END */ +/* harmony import */ var _util_subscribeToArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314); +/* harmony import */ var _scheduled_scheduleArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(315); +/** PURE_IMPORTS_START _Observable,_util_subscribeToArray,_scheduled_scheduleArray PURE_IMPORTS_END */ function fromArray(input, scheduler) { if (!scheduler) { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeToArray__WEBPACK_IMPORTED_MODULE_2__["subscribeToArray"])(input)); + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeToArray__WEBPACK_IMPORTED_MODULE_1__["subscribeToArray"])(input)); } else { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { - var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - var i = 0; - sub.add(scheduler.schedule(function () { - if (i === input.length) { - subscriber.complete(); - return; - } - subscriber.next(input[i++]); - if (!subscriber.closed) { - sub.add(this.schedule()); - } - })); - return sub; - }); + return Object(_scheduled_scheduleArray__WEBPACK_IMPORTED_MODULE_2__["scheduleArray"])(input, scheduler); } } //# sourceMappingURL=fromArray.js.map /***/ }), -/* 315 */ +/* 314 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -35982,38 +35970,46 @@ var subscribeToArray = function (array) { for (var i = 0, len = array.length; i < len && !subscriber.closed; i++) { subscriber.next(array[i]); } - if (!subscriber.closed) { - subscriber.complete(); - } + subscriber.complete(); }; }; //# sourceMappingURL=subscribeToArray.js.map /***/ }), -/* 316 */ +/* 315 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scalar", function() { return scalar; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scheduleArray", function() { return scheduleArray; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); +/** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ -function scalar(value) { - var result = new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { - subscriber.next(value); - subscriber.complete(); + +function scheduleArray(input, scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { + var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + var i = 0; + sub.add(scheduler.schedule(function () { + if (i === input.length) { + subscriber.complete(); + return; + } + subscriber.next(input[i++]); + if (!subscriber.closed) { + sub.add(this.schedule()); + } + })); + return sub; }); - result._isScalar = true; - result.value = value; - return result; } -//# sourceMappingURL=scalar.js.map +//# sourceMappingURL=scheduleArray.js.map /***/ }), -/* 317 */ +/* 316 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36038,14 +36034,14 @@ function dispatch(_a) { /***/ }), -/* 318 */ +/* 317 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsyncSubject", function() { return AsyncSubject; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(284); /** PURE_IMPORTS_START tslib,_Subject,_Subscription PURE_IMPORTS_END */ @@ -36097,14 +36093,14 @@ var AsyncSubject = /*@__PURE__*/ (function (_super) { /***/ }), -/* 319 */ +/* 318 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "asap", function() { return asap; }); -/* harmony import */ var _AsapAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(320); -/* harmony import */ var _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); +/* harmony import */ var _AsapAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(319); +/* harmony import */ var _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(321); /** PURE_IMPORTS_START _AsapAction,_AsapScheduler PURE_IMPORTS_END */ @@ -36113,15 +36109,15 @@ var asap = /*@__PURE__*/ new _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__["AsapSc /***/ }), -/* 320 */ +/* 319 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsapAction", function() { return AsapAction; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_Immediate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(321); -/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(304); +/* harmony import */ var _util_Immediate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(320); +/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(303); /** PURE_IMPORTS_START tslib,_util_Immediate,_AsyncAction PURE_IMPORTS_END */ @@ -36164,7 +36160,7 @@ var AsapAction = /*@__PURE__*/ (function (_super) { /***/ }), -/* 321 */ +/* 320 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36194,14 +36190,14 @@ var Immediate = { /***/ }), -/* 322 */ +/* 321 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsapScheduler", function() { return AsapScheduler; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(307); +/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(306); /** PURE_IMPORTS_START tslib,_AsyncScheduler PURE_IMPORTS_END */ @@ -36238,14 +36234,14 @@ var AsapScheduler = /*@__PURE__*/ (function (_super) { /***/ }), -/* 323 */ +/* 322 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "async", function() { return async; }); -/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(304); -/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(307); +/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(303); +/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(306); /** PURE_IMPORTS_START _AsyncAction,_AsyncScheduler PURE_IMPORTS_END */ @@ -36254,14 +36250,14 @@ var async = /*@__PURE__*/ new _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__["Asyn /***/ }), -/* 324 */ +/* 323 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "animationFrame", function() { return animationFrame; }); -/* harmony import */ var _AnimationFrameAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(325); -/* harmony import */ var _AnimationFrameScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(326); +/* harmony import */ var _AnimationFrameAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(324); +/* harmony import */ var _AnimationFrameScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(325); /** PURE_IMPORTS_START _AnimationFrameAction,_AnimationFrameScheduler PURE_IMPORTS_END */ @@ -36270,14 +36266,14 @@ var animationFrame = /*@__PURE__*/ new _AnimationFrameScheduler__WEBPACK_IMPORTE /***/ }), -/* 325 */ +/* 324 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AnimationFrameAction", function() { return AnimationFrameAction; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(304); +/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(303); /** PURE_IMPORTS_START tslib,_AsyncAction PURE_IMPORTS_END */ @@ -36319,14 +36315,14 @@ var AnimationFrameAction = /*@__PURE__*/ (function (_super) { /***/ }), -/* 326 */ +/* 325 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AnimationFrameScheduler", function() { return AnimationFrameScheduler; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(307); +/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(306); /** PURE_IMPORTS_START tslib,_AsyncScheduler PURE_IMPORTS_END */ @@ -36363,7 +36359,7 @@ var AnimationFrameScheduler = /*@__PURE__*/ (function (_super) { /***/ }), -/* 327 */ +/* 326 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36371,8 +36367,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualTimeScheduler", function() { return VirtualTimeScheduler; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualAction", function() { return VirtualAction; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(304); -/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(307); +/* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(303); +/* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(306); /** PURE_IMPORTS_START tslib,_AsyncAction,_AsyncScheduler PURE_IMPORTS_END */ @@ -36395,7 +36391,9 @@ var VirtualTimeScheduler = /*@__PURE__*/ (function (_super) { VirtualTimeScheduler.prototype.flush = function () { var _a = this, actions = _a.actions, maxFrames = _a.maxFrames; var error, action; - while ((action = actions.shift()) && (this.frame = action.delay) <= maxFrames) { + while ((action = actions[0]) && action.delay <= maxFrames) { + actions.shift(); + this.frame = action.delay; if (error = action.execute(action.state, action.delay)) { break; } @@ -36484,7 +36482,7 @@ var VirtualAction = /*@__PURE__*/ (function (_super) { /***/ }), -/* 328 */ +/* 327 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36498,7 +36496,7 @@ function identity(x) { /***/ }), -/* 329 */ +/* 328 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36514,90 +36512,86 @@ function isObservable(obj) { /***/ }), -/* 330 */ +/* 329 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ArgumentOutOfRangeError", function() { return ArgumentOutOfRangeError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/** PURE_IMPORTS_START tslib PURE_IMPORTS_END */ - -var ArgumentOutOfRangeError = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ArgumentOutOfRangeError, _super); - function ArgumentOutOfRangeError() { - var _this = _super.call(this, 'argument out of range') || this; - _this.name = 'ArgumentOutOfRangeError'; - Object.setPrototypeOf(_this, ArgumentOutOfRangeError.prototype); - return _this; +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +var ArgumentOutOfRangeErrorImpl = /*@__PURE__*/ (function () { + function ArgumentOutOfRangeErrorImpl() { + Error.call(this); + this.message = 'argument out of range'; + this.name = 'ArgumentOutOfRangeError'; + return this; } - return ArgumentOutOfRangeError; -}(Error)); - + ArgumentOutOfRangeErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return ArgumentOutOfRangeErrorImpl; +})(); +var ArgumentOutOfRangeError = ArgumentOutOfRangeErrorImpl; //# sourceMappingURL=ArgumentOutOfRangeError.js.map /***/ }), -/* 331 */ +/* 330 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EmptyError", function() { return EmptyError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/** PURE_IMPORTS_START tslib PURE_IMPORTS_END */ - -var EmptyError = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](EmptyError, _super); - function EmptyError() { - var _this = _super.call(this, 'no elements in sequence') || this; - _this.name = 'EmptyError'; - Object.setPrototypeOf(_this, EmptyError.prototype); - return _this; +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +var EmptyErrorImpl = /*@__PURE__*/ (function () { + function EmptyErrorImpl() { + Error.call(this); + this.message = 'no elements in sequence'; + this.name = 'EmptyError'; + return this; } - return EmptyError; -}(Error)); - + EmptyErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return EmptyErrorImpl; +})(); +var EmptyError = EmptyErrorImpl; //# sourceMappingURL=EmptyError.js.map /***/ }), -/* 332 */ +/* 331 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeoutError", function() { return TimeoutError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/** PURE_IMPORTS_START tslib PURE_IMPORTS_END */ - -var TimeoutError = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TimeoutError, _super); - function TimeoutError() { - var _this = _super.call(this, 'Timeout has occurred') || this; - _this.name = 'TimeoutError'; - Object.setPrototypeOf(_this, TimeoutError.prototype); - return _this; +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +var TimeoutErrorImpl = /*@__PURE__*/ (function () { + function TimeoutErrorImpl() { + Error.call(this); + this.message = 'Timeout has occurred'; + this.name = 'TimeoutError'; + return this; } - return TimeoutError; -}(Error)); - + TimeoutErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return TimeoutErrorImpl; +})(); +var TimeoutError = TimeoutErrorImpl; //# sourceMappingURL=TimeoutError.js.map /***/ }), -/* 333 */ +/* 332 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bindCallback", function() { return bindCallback; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(318); -/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(334); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(285); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(313); -/** PURE_IMPORTS_START _Observable,_AsyncSubject,_operators_map,_util_isArray,_util_isScheduler PURE_IMPORTS_END */ +/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(317); +/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(333); +/* harmony import */ var _util_canReportError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(277); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(285); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(312); +/** PURE_IMPORTS_START _Observable,_AsyncSubject,_operators_map,_util_canReportError,_util_isArray,_util_isScheduler PURE_IMPORTS_END */ + @@ -36605,7 +36599,7 @@ __webpack_require__.r(__webpack_exports__); function bindCallback(callbackFunc, resultSelector, scheduler) { if (resultSelector) { - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_4__["isScheduler"])(resultSelector)) { + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(resultSelector)) { scheduler = resultSelector; } else { @@ -36614,7 +36608,7 @@ function bindCallback(callbackFunc, resultSelector, scheduler) { for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } - return bindCallback(callbackFunc, scheduler).apply(void 0, args).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_2__["map"])(function (args) { return Object(_util_isArray__WEBPACK_IMPORTED_MODULE_3__["isArray"])(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); + return bindCallback(callbackFunc, scheduler).apply(void 0, args).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_2__["map"])(function (args) { return Object(_util_isArray__WEBPACK_IMPORTED_MODULE_4__["isArray"])(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); }; } } @@ -36647,7 +36641,12 @@ function bindCallback(callbackFunc, resultSelector, scheduler) { callbackFunc.apply(context, args.concat([handler])); } catch (err) { - subject.error(err); + if (Object(_util_canReportError__WEBPACK_IMPORTED_MODULE_3__["canReportError"])(subject)) { + subject.error(err); + } + else { + console.warn(err); + } } } return subject.subscribe(subscriber); @@ -36699,7 +36698,7 @@ function dispatchError(state) { /***/ }), -/* 334 */ +/* 333 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36756,18 +36755,20 @@ var MapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 335 */ +/* 334 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bindNodeCallback", function() { return bindNodeCallback; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(318); -/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(334); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(313); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(285); -/** PURE_IMPORTS_START _Observable,_AsyncSubject,_operators_map,_util_isScheduler,_util_isArray PURE_IMPORTS_END */ +/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(317); +/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(333); +/* harmony import */ var _util_canReportError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(277); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(312); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(285); +/** PURE_IMPORTS_START _Observable,_AsyncSubject,_operators_map,_util_canReportError,_util_isScheduler,_util_isArray PURE_IMPORTS_END */ + @@ -36775,7 +36776,7 @@ __webpack_require__.r(__webpack_exports__); function bindNodeCallback(callbackFunc, resultSelector, scheduler) { if (resultSelector) { - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_3__["isScheduler"])(resultSelector)) { + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_4__["isScheduler"])(resultSelector)) { scheduler = resultSelector; } else { @@ -36784,7 +36785,7 @@ function bindNodeCallback(callbackFunc, resultSelector, scheduler) { for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } - return bindNodeCallback(callbackFunc, scheduler).apply(void 0, args).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_2__["map"])(function (args) { return Object(_util_isArray__WEBPACK_IMPORTED_MODULE_4__["isArray"])(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); + return bindNodeCallback(callbackFunc, scheduler).apply(void 0, args).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_2__["map"])(function (args) { return Object(_util_isArray__WEBPACK_IMPORTED_MODULE_5__["isArray"])(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); }; } } @@ -36823,7 +36824,12 @@ function bindNodeCallback(callbackFunc, resultSelector, scheduler) { callbackFunc.apply(context, args.concat([handler])); } catch (err) { - subject.error(err); + if (Object(_util_canReportError__WEBPACK_IMPORTED_MODULE_3__["canReportError"])(subject)) { + subject.error(err); + } + else { + console.warn(err); + } } } return subject.subscribe(subscriber); @@ -36877,7 +36883,7 @@ function dispatchError(arg) { /***/ }), -/* 336 */ +/* 335 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -36886,11 +36892,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CombineLatestOperator", function() { return CombineLatestOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CombineLatestSubscriber", function() { return CombineLatestSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(313); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(312); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(285); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(314); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); +/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(313); /** PURE_IMPORTS_START tslib,_util_isScheduler,_util_isArray,_OuterSubscriber,_util_subscribeToResult,_fromArray PURE_IMPORTS_END */ @@ -36995,7 +37001,7 @@ var CombineLatestSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 337 */ +/* 336 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37027,26 +37033,36 @@ var OuterSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 338 */ +/* 337 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeToResult", function() { return subscribeToResult; }); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(339); -/* harmony import */ var _subscribeTo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(340); -/** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo PURE_IMPORTS_END */ +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(338); +/* harmony import */ var _subscribeTo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(339); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(276); +/** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo,_Observable PURE_IMPORTS_END */ + -function subscribeToResult(outerSubscriber, result, outerValue, outerIndex) { - var destination = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_0__["InnerSubscriber"](outerSubscriber, outerValue, outerIndex); +function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, destination) { + if (destination === void 0) { + destination = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_0__["InnerSubscriber"](outerSubscriber, outerValue, outerIndex); + } + if (destination.closed) { + return undefined; + } + if (result instanceof _Observable__WEBPACK_IMPORTED_MODULE_2__["Observable"]) { + return result.subscribe(destination); + } return Object(_subscribeTo__WEBPACK_IMPORTED_MODULE_1__["subscribeTo"])(result)(destination); } //# sourceMappingURL=subscribeToResult.js.map /***/ }), -/* 339 */ +/* 338 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37085,24 +37101,22 @@ var InnerSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 340 */ +/* 339 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeTo", function() { return subscribeTo; }); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _subscribeToArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(315); -/* harmony import */ var _subscribeToPromise__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(341); -/* harmony import */ var _subscribeToIterable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(342); -/* harmony import */ var _subscribeToObservable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(344); -/* harmony import */ var _isArrayLike__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(345); -/* harmony import */ var _isPromise__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(346); -/* harmony import */ var _isObject__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(286); -/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(343); -/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(291); -/** PURE_IMPORTS_START _Observable,_subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */ - +/* harmony import */ var _subscribeToArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(314); +/* harmony import */ var _subscribeToPromise__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(340); +/* harmony import */ var _subscribeToIterable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(341); +/* harmony import */ var _subscribeToObservable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(343); +/* harmony import */ var _isArrayLike__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(344); +/* harmony import */ var _isPromise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(345); +/* harmony import */ var _isObject__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(286); +/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(342); +/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(290); +/** PURE_IMPORTS_START _subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */ @@ -37113,32 +37127,20 @@ __webpack_require__.r(__webpack_exports__); var subscribeTo = function (result) { - if (result instanceof _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]) { - return function (subscriber) { - if (result._isScalar) { - subscriber.next(result.value); - subscriber.complete(); - return undefined; - } - else { - return result.subscribe(subscriber); - } - }; + if (!!result && typeof result[_symbol_observable__WEBPACK_IMPORTED_MODULE_8__["observable"]] === 'function') { + return Object(_subscribeToObservable__WEBPACK_IMPORTED_MODULE_3__["subscribeToObservable"])(result); } - else if (result && typeof result[_symbol_observable__WEBPACK_IMPORTED_MODULE_9__["observable"]] === 'function') { - return Object(_subscribeToObservable__WEBPACK_IMPORTED_MODULE_4__["subscribeToObservable"])(result); + else if (Object(_isArrayLike__WEBPACK_IMPORTED_MODULE_4__["isArrayLike"])(result)) { + return Object(_subscribeToArray__WEBPACK_IMPORTED_MODULE_0__["subscribeToArray"])(result); } - else if (Object(_isArrayLike__WEBPACK_IMPORTED_MODULE_5__["isArrayLike"])(result)) { - return Object(_subscribeToArray__WEBPACK_IMPORTED_MODULE_1__["subscribeToArray"])(result); + else if (Object(_isPromise__WEBPACK_IMPORTED_MODULE_5__["isPromise"])(result)) { + return Object(_subscribeToPromise__WEBPACK_IMPORTED_MODULE_1__["subscribeToPromise"])(result); } - else if (Object(_isPromise__WEBPACK_IMPORTED_MODULE_6__["isPromise"])(result)) { - return Object(_subscribeToPromise__WEBPACK_IMPORTED_MODULE_2__["subscribeToPromise"])(result); - } - else if (result && typeof result[_symbol_iterator__WEBPACK_IMPORTED_MODULE_8__["iterator"]] === 'function') { - return Object(_subscribeToIterable__WEBPACK_IMPORTED_MODULE_3__["subscribeToIterable"])(result); + else if (!!result && typeof result[_symbol_iterator__WEBPACK_IMPORTED_MODULE_7__["iterator"]] === 'function') { + return Object(_subscribeToIterable__WEBPACK_IMPORTED_MODULE_2__["subscribeToIterable"])(result); } else { - var value = Object(_isObject__WEBPACK_IMPORTED_MODULE_7__["isObject"])(result) ? 'an invalid object' : "'" + result + "'"; + var value = Object(_isObject__WEBPACK_IMPORTED_MODULE_6__["isObject"])(result) ? 'an invalid object' : "'" + result + "'"; var msg = "You provided " + value + " where a stream was expected." + ' You can provide an Observable, Promise, Array, or Iterable.'; throw new TypeError(msg); @@ -37148,7 +37150,7 @@ var subscribeTo = function (result) { /***/ }), -/* 341 */ +/* 340 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37173,13 +37175,13 @@ var subscribeToPromise = function (promise) { /***/ }), -/* 342 */ +/* 341 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeToIterable", function() { return subscribeToIterable; }); -/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(343); +/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(342); /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ var subscribeToIterable = function (iterable) { @@ -37210,7 +37212,7 @@ var subscribeToIterable = function (iterable) { /***/ }), -/* 343 */ +/* 342 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37231,13 +37233,13 @@ var $$iterator = iterator; /***/ }), -/* 344 */ +/* 343 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeToObservable", function() { return subscribeToObservable; }); -/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(291); +/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(290); /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ var subscribeToObservable = function (obj) { @@ -37255,7 +37257,7 @@ var subscribeToObservable = function (obj) { /***/ }), -/* 345 */ +/* 344 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37267,7 +37269,7 @@ var isArrayLike = (function (x) { return x && typeof x.length === 'number' && ty /***/ }), -/* 346 */ +/* 345 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37275,25 +37277,21 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isPromise", function() { return isPromise; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ function isPromise(value) { - return value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; + return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; } //# sourceMappingURL=isPromise.js.map /***/ }), -/* 347 */ +/* 346 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return concat; }); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(313); -/* harmony import */ var _of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(312); -/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(348); -/* harmony import */ var _operators_concatAll__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(354); -/** PURE_IMPORTS_START _util_isScheduler,_of,_from,_operators_concatAll PURE_IMPORTS_END */ - - +/* harmony import */ var _of__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(311); +/* harmony import */ var _operators_concatAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(347); +/** PURE_IMPORTS_START _of,_operators_concatAll PURE_IMPORTS_END */ function concat() { @@ -37301,249 +37299,19 @@ function concat() { for (var _i = 0; _i < arguments.length; _i++) { observables[_i] = arguments[_i]; } - if (observables.length === 1 || (observables.length === 2 && Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_0__["isScheduler"])(observables[1]))) { - return Object(_from__WEBPACK_IMPORTED_MODULE_2__["from"])(observables[0]); - } - return Object(_operators_concatAll__WEBPACK_IMPORTED_MODULE_3__["concatAll"])()(_of__WEBPACK_IMPORTED_MODULE_1__["of"].apply(void 0, observables)); + return Object(_operators_concatAll__WEBPACK_IMPORTED_MODULE_1__["concatAll"])()(_of__WEBPACK_IMPORTED_MODULE_0__["of"].apply(void 0, observables)); } //# sourceMappingURL=concat.js.map /***/ }), -/* 348 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "from", function() { return from; }); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(346); -/* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(345); -/* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(349); -/* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(350); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(314); -/* harmony import */ var _fromPromise__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(351); -/* harmony import */ var _fromIterable__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(352); -/* harmony import */ var _fromObservable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(353); -/* harmony import */ var _util_subscribeTo__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(340); -/** PURE_IMPORTS_START _Observable,_util_isPromise,_util_isArrayLike,_util_isInteropObservable,_util_isIterable,_fromArray,_fromPromise,_fromIterable,_fromObservable,_util_subscribeTo PURE_IMPORTS_END */ - - - - - - - - - - -function from(input, scheduler) { - if (!scheduler) { - if (input instanceof _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]) { - return input; - } - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeTo__WEBPACK_IMPORTED_MODULE_9__["subscribeTo"])(input)); - } - if (input != null) { - if (Object(_util_isInteropObservable__WEBPACK_IMPORTED_MODULE_3__["isInteropObservable"])(input)) { - return Object(_fromObservable__WEBPACK_IMPORTED_MODULE_8__["fromObservable"])(input, scheduler); - } - else if (Object(_util_isPromise__WEBPACK_IMPORTED_MODULE_1__["isPromise"])(input)) { - return Object(_fromPromise__WEBPACK_IMPORTED_MODULE_6__["fromPromise"])(input, scheduler); - } - else if (Object(_util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__["isArrayLike"])(input)) { - return Object(_fromArray__WEBPACK_IMPORTED_MODULE_5__["fromArray"])(input, scheduler); - } - else if (Object(_util_isIterable__WEBPACK_IMPORTED_MODULE_4__["isIterable"])(input) || typeof input === 'string') { - return Object(_fromIterable__WEBPACK_IMPORTED_MODULE_7__["fromIterable"])(input, scheduler); - } - } - throw new TypeError((input !== null && typeof input || input) + ' is not observable'); -} -//# sourceMappingURL=from.js.map - - -/***/ }), -/* 349 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isInteropObservable", function() { return isInteropObservable; }); -/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(291); -/** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ - -function isInteropObservable(input) { - return input && typeof input[_symbol_observable__WEBPACK_IMPORTED_MODULE_0__["observable"]] === 'function'; -} -//# sourceMappingURL=isInteropObservable.js.map - - -/***/ }), -/* 350 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isIterable", function() { return isIterable; }); -/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(343); -/** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ - -function isIterable(input) { - return input && typeof input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_0__["iterator"]] === 'function'; -} -//# sourceMappingURL=isIterable.js.map - - -/***/ }), -/* 351 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromPromise", function() { return fromPromise; }); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _util_subscribeToPromise__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(341); -/** PURE_IMPORTS_START _Observable,_Subscription,_util_subscribeToPromise PURE_IMPORTS_END */ - - - -function fromPromise(input, scheduler) { - if (!scheduler) { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeToPromise__WEBPACK_IMPORTED_MODULE_2__["subscribeToPromise"])(input)); - } - else { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { - var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - sub.add(scheduler.schedule(function () { - return input.then(function (value) { - sub.add(scheduler.schedule(function () { - subscriber.next(value); - sub.add(scheduler.schedule(function () { return subscriber.complete(); })); - })); - }, function (err) { - sub.add(scheduler.schedule(function () { return subscriber.error(err); })); - }); - })); - return sub; - }); - } -} -//# sourceMappingURL=fromPromise.js.map - - -/***/ }), -/* 352 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromIterable", function() { return fromIterable; }); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(343); -/* harmony import */ var _util_subscribeToIterable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(342); -/** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator,_util_subscribeToIterable PURE_IMPORTS_END */ - - - - -function fromIterable(input, scheduler) { - if (!input) { - throw new Error('Iterable cannot be null'); - } - if (!scheduler) { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeToIterable__WEBPACK_IMPORTED_MODULE_3__["subscribeToIterable"])(input)); - } - else { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { - var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - var iterator; - sub.add(function () { - if (iterator && typeof iterator.return === 'function') { - iterator.return(); - } - }); - sub.add(scheduler.schedule(function () { - iterator = input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_2__["iterator"]](); - sub.add(scheduler.schedule(function () { - if (subscriber.closed) { - return; - } - var value; - var done; - try { - var result = iterator.next(); - value = result.value; - done = result.done; - } - catch (err) { - subscriber.error(err); - return; - } - if (done) { - subscriber.complete(); - } - else { - subscriber.next(value); - this.schedule(); - } - })); - })); - return sub; - }); - } -} -//# sourceMappingURL=fromIterable.js.map - - -/***/ }), -/* 353 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromObservable", function() { return fromObservable; }); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(291); -/* harmony import */ var _util_subscribeToObservable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(344); -/** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable,_util_subscribeToObservable PURE_IMPORTS_END */ - - - - -function fromObservable(input, scheduler) { - if (!scheduler) { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeToObservable__WEBPACK_IMPORTED_MODULE_3__["subscribeToObservable"])(input)); - } - else { - return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { - var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - sub.add(scheduler.schedule(function () { - var observable = input[_symbol_observable__WEBPACK_IMPORTED_MODULE_2__["observable"]](); - sub.add(observable.subscribe({ - next: function (value) { sub.add(scheduler.schedule(function () { return subscriber.next(value); })); }, - error: function (err) { sub.add(scheduler.schedule(function () { return subscriber.error(err); })); }, - complete: function () { sub.add(scheduler.schedule(function () { return subscriber.complete(); })); }, - })); - })); - return sub; - }); - } -} -//# sourceMappingURL=fromObservable.js.map - - -/***/ }), -/* 354 */ +/* 347 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return concatAll; }); -/* harmony import */ var _mergeAll__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(355); +/* harmony import */ var _mergeAll__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(348); /** PURE_IMPORTS_START _mergeAll PURE_IMPORTS_END */ function concatAll() { @@ -37553,14 +37321,14 @@ function concatAll() { /***/ }), -/* 355 */ +/* 348 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return mergeAll; }); -/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(356); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(328); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(349); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(327); /** PURE_IMPORTS_START _mergeMap,_util_identity PURE_IMPORTS_END */ @@ -37574,7 +37342,7 @@ function mergeAll(concurrent) { /***/ }), -/* 356 */ +/* 349 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -37583,11 +37351,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeMapOperator", function() { return MergeMapOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeMapSubscriber", function() { return MergeMapSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(338); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(348); -/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_map,_observable_from PURE_IMPORTS_END */ +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(338); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(333); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(350); +/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber,_map,_observable_from PURE_IMPORTS_END */ + @@ -37598,7 +37368,7 @@ function mergeMap(project, resultSelector, concurrent) { concurrent = Number.POSITIVE_INFINITY; } if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(mergeMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_4__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_3__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); }; + return function (source) { return source.pipe(mergeMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); }; } else if (typeof resultSelector === 'number') { concurrent = resultSelector; @@ -37656,13 +37426,17 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { this._innerSub(result, value, index); }; MergeMapSubscriber.prototype._innerSub = function (ish, value, index) { - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__["subscribeToResult"])(this, ish, value, index)); + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__["InnerSubscriber"](this, undefined, undefined); + var destination = this.destination; + destination.add(innerSubscriber); + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__["subscribeToResult"])(this, ish, value, index, innerSubscriber); }; MergeMapSubscriber.prototype._complete = function () { this.hasCompleted = true; if (this.active === 0 && this.buffer.length === 0) { this.destination.complete(); } + this.unsubscribe(); }; MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this.destination.next(innerValue); @@ -37684,6 +37458,230 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { //# sourceMappingURL=mergeMap.js.map +/***/ }), +/* 350 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "from", function() { return from; }); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); +/* harmony import */ var _util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(339); +/* harmony import */ var _scheduled_scheduled__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(351); +/** PURE_IMPORTS_START _Observable,_util_subscribeTo,_scheduled_scheduled PURE_IMPORTS_END */ + + + +function from(input, scheduler) { + if (!scheduler) { + if (input instanceof _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]) { + return input; + } + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](Object(_util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__["subscribeTo"])(input)); + } + else { + return Object(_scheduled_scheduled__WEBPACK_IMPORTED_MODULE_2__["scheduled"])(input, scheduler); + } +} +//# sourceMappingURL=from.js.map + + +/***/ }), +/* 351 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scheduled", function() { return scheduled; }); +/* harmony import */ var _scheduleObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(352); +/* harmony import */ var _schedulePromise__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(353); +/* harmony import */ var _scheduleArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(315); +/* harmony import */ var _scheduleIterable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(354); +/* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(355); +/* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(345); +/* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(344); +/* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(356); +/** PURE_IMPORTS_START _scheduleObservable,_schedulePromise,_scheduleArray,_scheduleIterable,_util_isInteropObservable,_util_isPromise,_util_isArrayLike,_util_isIterable PURE_IMPORTS_END */ + + + + + + + + +function scheduled(input, scheduler) { + if (input != null) { + if (Object(_util_isInteropObservable__WEBPACK_IMPORTED_MODULE_4__["isInteropObservable"])(input)) { + return Object(_scheduleObservable__WEBPACK_IMPORTED_MODULE_0__["scheduleObservable"])(input, scheduler); + } + else if (Object(_util_isPromise__WEBPACK_IMPORTED_MODULE_5__["isPromise"])(input)) { + return Object(_schedulePromise__WEBPACK_IMPORTED_MODULE_1__["schedulePromise"])(input, scheduler); + } + else if (Object(_util_isArrayLike__WEBPACK_IMPORTED_MODULE_6__["isArrayLike"])(input)) { + return Object(_scheduleArray__WEBPACK_IMPORTED_MODULE_2__["scheduleArray"])(input, scheduler); + } + else if (Object(_util_isIterable__WEBPACK_IMPORTED_MODULE_7__["isIterable"])(input) || typeof input === 'string') { + return Object(_scheduleIterable__WEBPACK_IMPORTED_MODULE_3__["scheduleIterable"])(input, scheduler); + } + } + throw new TypeError((input !== null && typeof input || input) + ' is not observable'); +} +//# sourceMappingURL=scheduled.js.map + + +/***/ }), +/* 352 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scheduleObservable", function() { return scheduleObservable; }); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); +/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(290); +/** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable PURE_IMPORTS_END */ + + + +function scheduleObservable(input, scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { + var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + sub.add(scheduler.schedule(function () { + var observable = input[_symbol_observable__WEBPACK_IMPORTED_MODULE_2__["observable"]](); + sub.add(observable.subscribe({ + next: function (value) { sub.add(scheduler.schedule(function () { return subscriber.next(value); })); }, + error: function (err) { sub.add(scheduler.schedule(function () { return subscriber.error(err); })); }, + complete: function () { sub.add(scheduler.schedule(function () { return subscriber.complete(); })); }, + })); + })); + return sub; + }); +} +//# sourceMappingURL=scheduleObservable.js.map + + +/***/ }), +/* 353 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "schedulePromise", function() { return schedulePromise; }); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); +/** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ + + +function schedulePromise(input, scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { + var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + sub.add(scheduler.schedule(function () { + return input.then(function (value) { + sub.add(scheduler.schedule(function () { + subscriber.next(value); + sub.add(scheduler.schedule(function () { return subscriber.complete(); })); + })); + }, function (err) { + sub.add(scheduler.schedule(function () { return subscriber.error(err); })); + }); + })); + return sub; + }); +} +//# sourceMappingURL=schedulePromise.js.map + + +/***/ }), +/* 354 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scheduleIterable", function() { return scheduleIterable; }); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); +/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(342); +/** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator PURE_IMPORTS_END */ + + + +function scheduleIterable(input, scheduler) { + if (!input) { + throw new Error('Iterable cannot be null'); + } + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { + var sub = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + var iterator; + sub.add(function () { + if (iterator && typeof iterator.return === 'function') { + iterator.return(); + } + }); + sub.add(scheduler.schedule(function () { + iterator = input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_2__["iterator"]](); + sub.add(scheduler.schedule(function () { + if (subscriber.closed) { + return; + } + var value; + var done; + try { + var result = iterator.next(); + value = result.value; + done = result.done; + } + catch (err) { + subscriber.error(err); + return; + } + if (done) { + subscriber.complete(); + } + else { + subscriber.next(value); + this.schedule(); + } + })); + })); + return sub; + }); +} +//# sourceMappingURL=scheduleIterable.js.map + + +/***/ }), +/* 355 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isInteropObservable", function() { return isInteropObservable; }); +/* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(290); +/** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ + +function isInteropObservable(input) { + return input && typeof input[_symbol_observable__WEBPACK_IMPORTED_MODULE_0__["observable"]] === 'function'; +} +//# sourceMappingURL=isInteropObservable.js.map + + +/***/ }), +/* 356 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isIterable", function() { return isIterable; }); +/* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(342); +/** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ + +function isIterable(input) { + return input && typeof input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_0__["iterator"]] === 'function'; +} +//# sourceMappingURL=isIterable.js.map + + /***/ }), /* 357 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -37692,8 +37690,8 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return defer; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(348); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); +/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(350); +/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(310); /** PURE_IMPORTS_START _Observable,_from,_empty PURE_IMPORTS_END */ @@ -37722,16 +37720,12 @@ function defer(observableFactory) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "forkJoin", function() { return forkJoin; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(276); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(285); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(311); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(337); -/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(334); -/** PURE_IMPORTS_START tslib,_Observable,_util_isArray,_empty,_util_subscribeToResult,_OuterSubscriber,_operators_map PURE_IMPORTS_END */ - - +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(285); +/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(333); +/* harmony import */ var _util_isObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(286); +/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(350); +/** PURE_IMPORTS_START _Observable,_util_isArray,_operators_map,_util_isObject,_from PURE_IMPORTS_END */ @@ -37742,66 +37736,63 @@ function forkJoin() { for (var _i = 0; _i < arguments.length; _i++) { sources[_i] = arguments[_i]; } - var resultSelector; - if (typeof sources[sources.length - 1] === 'function') { - resultSelector = sources.pop(); - } - if (sources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(sources[0])) { - sources = sources[0]; - } - if (sources.length === 0) { - return _empty__WEBPACK_IMPORTED_MODULE_3__["EMPTY"]; + if (sources.length === 1) { + var first_1 = sources[0]; + if (Object(_util_isArray__WEBPACK_IMPORTED_MODULE_1__["isArray"])(first_1)) { + return forkJoinInternal(first_1, null); + } + if (Object(_util_isObject__WEBPACK_IMPORTED_MODULE_3__["isObject"])(first_1) && Object.getPrototypeOf(first_1) === Object.prototype) { + var keys = Object.keys(first_1); + return forkJoinInternal(keys.map(function (key) { return first_1[key]; }), keys); + } } - if (resultSelector) { - return forkJoin(sources).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_6__["map"])(function (args) { return resultSelector.apply(void 0, args); })); + if (typeof sources[sources.length - 1] === 'function') { + var resultSelector_1 = sources.pop(); + sources = (sources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_1__["isArray"])(sources[0])) ? sources[0] : sources; + return forkJoinInternal(sources, null).pipe(Object(_operators_map__WEBPACK_IMPORTED_MODULE_2__["map"])(function (args) { return resultSelector_1.apply(void 0, args); })); } - return new _Observable__WEBPACK_IMPORTED_MODULE_1__["Observable"](function (subscriber) { - return new ForkJoinSubscriber(subscriber, sources); - }); + return forkJoinInternal(sources, null); } -var ForkJoinSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ForkJoinSubscriber, _super); - function ForkJoinSubscriber(destination, sources) { - var _this = _super.call(this, destination) || this; - _this.sources = sources; - _this.completed = 0; - _this.haveValues = 0; +function forkJoinInternal(sources, keys) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { var len = sources.length; - _this.values = new Array(len); - for (var i = 0; i < len; i++) { - var source = sources[i]; - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(_this, source, null, i); - if (innerSubscription) { - _this.add(innerSubscription); - } - } - return _this; - } - ForkJoinSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.values[outerIndex] = innerValue; - if (!innerSub._hasValue) { - innerSub._hasValue = true; - this.haveValues++; - } - }; - ForkJoinSubscriber.prototype.notifyComplete = function (innerSub) { - var _a = this, destination = _a.destination, haveValues = _a.haveValues, values = _a.values; - var len = values.length; - if (!innerSub._hasValue) { - destination.complete(); - return; - } - this.completed++; - if (this.completed !== len) { + if (len === 0) { + subscriber.complete(); return; } - if (haveValues === len) { - destination.next(values); + var values = new Array(len); + var completed = 0; + var emitted = 0; + var _loop_1 = function (i) { + var source = Object(_from__WEBPACK_IMPORTED_MODULE_4__["from"])(sources[i]); + var hasValue = false; + subscriber.add(source.subscribe({ + next: function (value) { + if (!hasValue) { + hasValue = true; + emitted++; + } + values[i] = value; + }, + error: function (err) { return subscriber.error(err); }, + complete: function () { + completed++; + if (completed === len || !hasValue) { + if (emitted === len) { + subscriber.next(keys ? + keys.reduce(function (result, key, i) { return (result[key] = values[i], result); }, {}) : + values); + } + subscriber.complete(); + } + } + })); + }; + for (var i = 0; i < len; i++) { + _loop_1(i); } - destination.complete(); - }; - return ForkJoinSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_5__["OuterSubscriber"])); + }); +} //# sourceMappingURL=forkJoin.js.map @@ -37815,13 +37806,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(285); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(280); -/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); +/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); /** PURE_IMPORTS_START _Observable,_util_isArray,_util_isFunction,_operators_map PURE_IMPORTS_END */ -var toString = Object.prototype.toString; +var toString = /*@__PURE__*/ (function () { return Object.prototype.toString; })(); function fromEvent(target, eventName, options, resultSelector) { if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_2__["isFunction"])(options)) { resultSelector = options; @@ -37891,7 +37882,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(285); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(280); -/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); +/* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); /** PURE_IMPORTS_START _Observable,_util_isArray,_util_isFunction,_operators_map PURE_IMPORTS_END */ @@ -37934,8 +37925,8 @@ function fromEventPattern(addHandler, removeHandler, resultSelector) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generate", function() { return generate; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(328); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(313); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(327); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(312); /** PURE_IMPORTS_START _Observable,_util_identity,_util_isScheduler PURE_IMPORTS_END */ @@ -38071,7 +38062,7 @@ function dispatch(state) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "iif", function() { return iif; }); /* harmony import */ var _defer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(357); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(311); +/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(310); /** PURE_IMPORTS_START _defer,_empty PURE_IMPORTS_END */ @@ -38095,7 +38086,7 @@ function iif(condition, trueResult, falseResult) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "interval", function() { return interval; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(323); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); /* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(364); /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric PURE_IMPORTS_END */ @@ -38151,9 +38142,9 @@ function isNumeric(val) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return merge; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(313); -/* harmony import */ var _operators_mergeAll__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(355); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(314); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(312); +/* harmony import */ var _operators_mergeAll__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(348); +/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(313); /** PURE_IMPORTS_START _Observable,_util_isScheduler,_operators_mergeAll,_fromArray PURE_IMPORTS_END */ @@ -38193,7 +38184,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NEVER", function() { return NEVER; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "never", function() { return never; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(293); +/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(292); /** PURE_IMPORTS_START _Observable,_util_noop PURE_IMPORTS_END */ @@ -38212,9 +38203,9 @@ function never() { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return onErrorResumeNext; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(348); +/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(350); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(285); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(311); +/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(310); /** PURE_IMPORTS_START _Observable,_from,_util_isArray,_empty PURE_IMPORTS_END */ @@ -38299,6 +38290,104 @@ function dispatch(state) { /* 369 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return partition; }); +/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(370); +/* harmony import */ var _util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(339); +/* harmony import */ var _operators_filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(371); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(276); +/** PURE_IMPORTS_START _util_not,_util_subscribeTo,_operators_filter,_Observable PURE_IMPORTS_END */ + + + + +function partition(source, predicate, thisArg) { + return [ + Object(_operators_filter__WEBPACK_IMPORTED_MODULE_2__["filter"])(predicate, thisArg)(new _Observable__WEBPACK_IMPORTED_MODULE_3__["Observable"](Object(_util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__["subscribeTo"])(source))), + Object(_operators_filter__WEBPACK_IMPORTED_MODULE_2__["filter"])(Object(_util_not__WEBPACK_IMPORTED_MODULE_0__["not"])(predicate, thisArg))(new _Observable__WEBPACK_IMPORTED_MODULE_3__["Observable"](Object(_util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__["subscribeTo"])(source))) + ]; +} +//# sourceMappingURL=partition.js.map + + +/***/ }), +/* 370 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "not", function() { return not; }); +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +function not(pred, thisArg) { + function notPred() { + return !(notPred.pred.apply(notPred.thisArg, arguments)); + } + notPred.pred = pred; + notPred.thisArg = thisArg; + return notPred; +} +//# sourceMappingURL=not.js.map + + +/***/ }), +/* 371 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return filter; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + + +function filter(predicate, thisArg) { + return function filterOperatorFunction(source) { + return source.lift(new FilterOperator(predicate, thisArg)); + }; +} +var FilterOperator = /*@__PURE__*/ (function () { + function FilterOperator(predicate, thisArg) { + this.predicate = predicate; + this.thisArg = thisArg; + } + FilterOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); + }; + return FilterOperator; +}()); +var FilterSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FilterSubscriber, _super); + function FilterSubscriber(destination, predicate, thisArg) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.thisArg = thisArg; + _this.count = 0; + return _this; + } + FilterSubscriber.prototype._next = function (value) { + var result; + try { + result = this.predicate.call(this.thisArg, value, this.count++); + } + catch (err) { + this.destination.error(err); + return; + } + if (result) { + this.destination.next(value); + } + }; + return FilterSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=filter.js.map + + +/***/ }), +/* 372 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "race", function() { return race; }); @@ -38306,9 +38395,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RaceSubscriber", function() { return RaceSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(285); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(314); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); +/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(313); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_util_isArray,_fromArray,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -38390,7 +38479,7 @@ var RaceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 370 */ +/* 373 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -38404,10 +38493,11 @@ function range(start, count, scheduler) { if (start === void 0) { start = 0; } - if (count === void 0) { - count = 0; - } return new _Observable__WEBPACK_IMPORTED_MODULE_0__["Observable"](function (subscriber) { + if (count === undefined) { + count = start; + start = 0; + } var index = 0; var current = start; if (scheduler) { @@ -38448,16 +38538,16 @@ function dispatch(state) { /***/ }), -/* 371 */ +/* 374 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timer", function() { return timer; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(323); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); /* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(364); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(313); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(312); /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ @@ -38502,15 +38592,15 @@ function dispatch(state) { /***/ }), -/* 372 */ +/* 375 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "using", function() { return using; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); -/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(348); -/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); +/* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(350); +/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(310); /** PURE_IMPORTS_START _Observable,_from,_empty PURE_IMPORTS_END */ @@ -38547,7 +38637,7 @@ function using(resourceFactory, observableFactory) { /***/ }), -/* 373 */ +/* 376 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -38556,12 +38646,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ZipOperator", function() { return ZipOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ZipSubscriber", function() { return ZipSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314); +/* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(313); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(285); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(278); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(338); -/* harmony import */ var _internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(337); +/* harmony import */ var _internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(342); /** PURE_IMPORTS_START tslib,_fromArray,_util_isArray,_Subscriber,_OuterSubscriber,_util_subscribeToResult,_.._internal_symbol_iterator PURE_IMPORTS_END */ @@ -38619,6 +38709,7 @@ var ZipSubscriber = /*@__PURE__*/ (function (_super) { ZipSubscriber.prototype._complete = function () { var iterators = this.iterators; var len = iterators.length; + this.unsubscribe(); if (len === 0) { this.destination.complete(); return; @@ -38627,7 +38718,8 @@ var ZipSubscriber = /*@__PURE__*/ (function (_super) { for (var i = 0; i < len; i++) { var iterator = iterators[i]; if (iterator.stillUnsubscribed) { - this.add(iterator.subscribe(iterator, i)); + var destination = this.destination; + destination.add(iterator.subscribe(iterator, i)); } else { this.active--; @@ -38781,173 +38873,173 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { /***/ }), -/* 374 */ +/* 377 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(375); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(378); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(376); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(379); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(377); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(380); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(378); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(381); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(379); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(382); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(380); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(383); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(381); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(384); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(382); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(385); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(383); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(386); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(384); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(387); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(385); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(388); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); -/* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(354); +/* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(347); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(386); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(389); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(387); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(390); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(388); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(391); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(389); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(392); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(390); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(393); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(391); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(394); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(392); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(395); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(394); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(397); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(395); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(398); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(396); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(399); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(397); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(400); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(398); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(401); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(399); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(402); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(404); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(405); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(405); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(406); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(406); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(407); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(407); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(408); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(408); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(409); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); -/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(400); +/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(371); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(409); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(410); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(410); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(411); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(411); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(412); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(412); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(413); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); -/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(299); +/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(298); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(413); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(414); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(414); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(415); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(415); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(416); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); -/* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(334); +/* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(333); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(417); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(418); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(418); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(419); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(419); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(420); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(422); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); -/* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(355); +/* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(348); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__["mergeAll"]; }); -/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(356); +/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(349); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(423); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(424); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(425); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(426); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(426); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(427); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); -/* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(309); +/* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(308); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(427); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(428); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(429); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); /* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(431); @@ -38968,7 +39060,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(420); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(421); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); /* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(437); @@ -38983,7 +39075,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); -/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(298); +/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(297); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); /* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(441); @@ -38992,7 +39084,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(421); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(422); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); /* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(443); @@ -39034,10 +39126,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(403); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(404); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(416); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(417); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); /* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(457); @@ -39046,55 +39138,55 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(402); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(401); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(403); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -39206,20 +39298,16 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 375 */ +/* 378 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return audit; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -39249,18 +39337,20 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { this.value = value; this.hasValue = true; if (!this.throttled) { - var duration = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_1__["tryCatch"])(this.durationSelector)(value); - if (duration === _util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"]) { - this.destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"].e); + var duration = void 0; + try { + var durationSelector = this.durationSelector; + duration = durationSelector(value); + } + catch (err) { + return this.destination.error(err); + } + var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, duration); + if (!innerSubscription || innerSubscription.closed) { + this.clearThrottle(); } else { - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, duration); - if (!innerSubscription || innerSubscription.closed) { - this.clearThrottle(); - } - else { - this.add(this.throttled = innerSubscription); - } + this.add(this.throttled = innerSubscription); } } }; @@ -39284,20 +39374,20 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { this.clearThrottle(); }; return AuditSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); //# sourceMappingURL=audit.js.map /***/ }), -/* 376 */ +/* 379 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(323); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(375); -/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(371); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(322); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(378); +/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(374); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -39312,15 +39402,15 @@ function auditTime(duration, scheduler) { /***/ }), -/* 377 */ +/* 380 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return buffer; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -39361,7 +39451,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 378 */ +/* 381 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -39462,16 +39552,16 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 379 */ +/* 382 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return bufferTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(323); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(278); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(313); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(312); /** PURE_IMPORTS_START tslib,_scheduler_async,_Subscriber,_util_isScheduler PURE_IMPORTS_END */ @@ -39623,7 +39713,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 380 */ +/* 383 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -39631,8 +39721,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return bufferToggle; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); /** PURE_IMPORTS_START tslib,_Subscription,_util_subscribeToResult,_OuterSubscriber PURE_IMPORTS_END */ @@ -39743,7 +39833,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 381 */ +/* 384 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -39751,13 +39841,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return bufferWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_Subscription,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -39821,35 +39907,39 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { this.destination.next(buffer); } this.buffer = []; - var closingNotifier = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.closingSelector)(); - if (closingNotifier === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - this.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e); + var closingNotifier; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(); } - else { - closingSubscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - this.closingSubscription = closingSubscription; - this.add(closingSubscription); - this.subscribing = true; - closingSubscription.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, closingNotifier)); - this.subscribing = false; + catch (err) { + return this.error(err); } + closingSubscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + this.closingSubscription = closingSubscription; + this.add(closingSubscription); + this.subscribing = true; + closingSubscription.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, closingNotifier)); + this.subscribing = false; }; return BufferWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); //# sourceMappingURL=bufferWhen.js.map /***/ }), -/* 382 */ +/* 385 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return catchError; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + @@ -39888,7 +39978,9 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { return; } this._unsubscribeAndRecycle(); - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, result)); + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, undefined, undefined); + this.add(innerSubscriber); + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, undefined, undefined, innerSubscriber); } }; return CatchSubscriber; @@ -39897,13 +39989,13 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 383 */ +/* 386 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return combineAll; }); -/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(336); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(335); /** PURE_IMPORTS_START _observable_combineLatest PURE_IMPORTS_END */ function combineAll(project) { @@ -39913,15 +40005,15 @@ function combineAll(project) { /***/ }), -/* 384 */ +/* 387 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return combineLatest; }); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(285); -/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(348); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(335); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(350); /** PURE_IMPORTS_START _util_isArray,_observable_combineLatest,_observable_from PURE_IMPORTS_END */ @@ -39945,13 +40037,13 @@ function combineLatest() { /***/ }), -/* 385 */ +/* 388 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return concat; }); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(347); +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(346); /** PURE_IMPORTS_START _observable_concat PURE_IMPORTS_END */ function concat() { @@ -39965,13 +40057,13 @@ function concat() { /***/ }), -/* 386 */ +/* 389 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return concatMap; }); -/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(356); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(349); /** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ function concatMap(project, resultSelector) { @@ -39981,13 +40073,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 387 */ +/* 390 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(386); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(389); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -39997,7 +40089,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 388 */ +/* 391 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40062,15 +40154,15 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 389 */ +/* 392 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return debounce; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -40150,7 +40242,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 390 */ +/* 393 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40158,7 +40250,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return debounceTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(323); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(322); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ @@ -40226,7 +40318,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 391 */ +/* 394 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40276,17 +40368,17 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 392 */ +/* 395 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(323); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(393); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(396); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(278); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(310); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(309); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -40341,7 +40433,8 @@ var DelaySubscriber = /*@__PURE__*/ (function (_super) { }; DelaySubscriber.prototype._schedule = function (scheduler) { this.active = true; - this.add(scheduler.schedule(DelaySubscriber.dispatch, this.delay, { + var destination = this.destination; + destination.add(scheduler.schedule(DelaySubscriber.dispatch, this.delay, { source: this, destination: this.destination, scheduler: scheduler })); }; @@ -40363,9 +40456,11 @@ var DelaySubscriber = /*@__PURE__*/ (function (_super) { this.errored = true; this.queue = []; this.destination.error(err); + this.unsubscribe(); }; DelaySubscriber.prototype._complete = function () { this.scheduleNotification(_Notification__WEBPACK_IMPORTED_MODULE_4__["Notification"].createComplete()); + this.unsubscribe(); }; return DelaySubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_3__["Subscriber"])); @@ -40380,7 +40475,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 393 */ +/* 396 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40394,7 +40489,7 @@ function isDate(value) { /***/ }), -/* 394 */ +/* 397 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40403,8 +40498,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(276); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -40436,6 +40531,7 @@ var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { _this.delayDurationSelector = delayDurationSelector; _this.completed = false; _this.delayNotifierSubscriptions = []; + _this.index = 0; return _this; } DelayWhenSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { @@ -40454,8 +40550,9 @@ var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { this.tryComplete(); }; DelayWhenSubscriber.prototype._next = function (value) { + var index = this.index++; try { - var delayNotifier = this.delayDurationSelector(value); + var delayNotifier = this.delayDurationSelector(value, index); if (delayNotifier) { this.tryDelay(delayNotifier, value); } @@ -40467,6 +40564,7 @@ var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { DelayWhenSubscriber.prototype._complete = function () { this.completed = true; this.tryComplete(); + this.unsubscribe(); }; DelayWhenSubscriber.prototype.removeSubscription = function (subscription) { subscription.unsubscribe(); @@ -40479,7 +40577,8 @@ var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { DelayWhenSubscriber.prototype.tryDelay = function (delayNotifier, value) { var notifierSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, delayNotifier, value); if (notifierSubscription && !notifierSubscription.closed) { - this.add(notifierSubscription); + var destination = this.destination; + destination.add(notifierSubscription); this.delayNotifierSubscriptions.push(notifierSubscription); } }; @@ -40520,6 +40619,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { this.parent.error(err); }; SubscriptionDelaySubscriber.prototype._complete = function () { + this.unsubscribe(); this.subscribeToSource(); }; SubscriptionDelaySubscriber.prototype.subscribeToSource = function () { @@ -40535,7 +40635,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 395 */ +/* 398 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40573,7 +40673,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 396 */ +/* 399 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40581,8 +40681,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return distinct; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DistinctSubscriber", function() { return DistinctSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -40651,7 +40751,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 397 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40659,11 +40759,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return distinctUntilChanged; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_tryCatch,_util_errorObject PURE_IMPORTS_END */ - - +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function distinctUntilChanged(compare, keySelector) { @@ -40694,25 +40790,28 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { return x === y; }; DistinctUntilChangedSubscriber.prototype._next = function (value) { - var keySelector = this.keySelector; - var key = value; - if (keySelector) { - key = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.keySelector)(value); - if (key === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - return this.destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e); - } + var key; + try { + var keySelector = this.keySelector; + key = keySelector ? keySelector(value) : value; + } + catch (err) { + return this.destination.error(err); } var result = false; if (this.hasKey) { - result = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.compare)(this.key, key); - if (result === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - return this.destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e); + try { + var compare = this.compare; + result = compare(this.key, key); + } + catch (err) { + return this.destination.error(err); } } else { this.hasKey = true; } - if (Boolean(result) === false) { + if (!result) { this.key = key; this.destination.next(value); } @@ -40723,13 +40822,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 398 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(397); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(400); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -40739,17 +40838,17 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 399 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(330); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(400); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(401); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(391); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(403); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(329); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(371); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(403); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(394); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(404); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -40771,181 +40870,73 @@ function elementAt(index, defaultValue) { /***/ }), -/* 400 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return filter; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return throwIfEmpty; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - - -function filter(predicate, thisArg) { - return function filterOperatorFunction(source) { - return source.lift(new FilterOperator(predicate, thisArg)); - }; -} -var FilterOperator = /*@__PURE__*/ (function () { - function FilterOperator(predicate, thisArg) { - this.predicate = predicate; - this.thisArg = thisArg; - } - FilterOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); - }; - return FilterOperator; -}()); -var FilterSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FilterSubscriber, _super); - function FilterSubscriber(destination, predicate, thisArg) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.thisArg = thisArg; - _this.count = 0; - return _this; - } - FilterSubscriber.prototype._next = function (value) { - var result; - try { - result = this.predicate.call(this.thisArg, value, this.count++); - } - catch (err) { - this.destination.error(err); - return; - } - if (result) { - this.destination.next(value); - } - }; - return FilterSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=filter.js.map - - -/***/ }), -/* 401 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(330); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(278); +/** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return throwIfEmpty; }); -/* harmony import */ var _tap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(402); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(331); -/** PURE_IMPORTS_START _tap,_util_EmptyError PURE_IMPORTS_END */ -var throwIfEmpty = function (errorFactory) { +function throwIfEmpty(errorFactory) { if (errorFactory === void 0) { errorFactory = defaultErrorFactory; } - return Object(_tap__WEBPACK_IMPORTED_MODULE_0__["tap"])({ - hasValue: false, - next: function () { this.hasValue = true; }, - complete: function () { - if (!this.hasValue) { - throw errorFactory(); - } - } - }); -}; -function defaultErrorFactory() { - return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__["EmptyError"](); -} -//# sourceMappingURL=throwIfEmpty.js.map - - -/***/ }), -/* 402 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return tap; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(293); -/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ - - - - -function tap(nextOrObserver, error, complete) { - return function tapOperatorFunction(source) { - return source.lift(new DoOperator(nextOrObserver, error, complete)); + return function (source) { + return source.lift(new ThrowIfEmptyOperator(errorFactory)); }; } -var DoOperator = /*@__PURE__*/ (function () { - function DoOperator(nextOrObserver, error, complete) { - this.nextOrObserver = nextOrObserver; - this.error = error; - this.complete = complete; +var ThrowIfEmptyOperator = /*@__PURE__*/ (function () { + function ThrowIfEmptyOperator(errorFactory) { + this.errorFactory = errorFactory; } - DoOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); + ThrowIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); }; - return DoOperator; + return ThrowIfEmptyOperator; }()); -var TapSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TapSubscriber, _super); - function TapSubscriber(destination, observerOrNext, error, complete) { +var ThrowIfEmptySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrowIfEmptySubscriber, _super); + function ThrowIfEmptySubscriber(destination, errorFactory) { var _this = _super.call(this, destination) || this; - _this._tapNext = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_3__["isFunction"])(observerOrNext)) { - _this._context = _this; - _this._tapNext = observerOrNext; - } - else if (observerOrNext) { - _this._context = observerOrNext; - _this._tapNext = observerOrNext.next || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = observerOrNext.error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = observerOrNext.complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - } + _this.errorFactory = errorFactory; + _this.hasValue = false; return _this; } - TapSubscriber.prototype._next = function (value) { - try { - this._tapNext.call(this._context, value); - } - catch (err) { - this.destination.error(err); - return; - } + ThrowIfEmptySubscriber.prototype._next = function (value) { + this.hasValue = true; this.destination.next(value); }; - TapSubscriber.prototype._error = function (err) { - try { - this._tapError.call(this._context, err); - } - catch (err) { + ThrowIfEmptySubscriber.prototype._complete = function () { + if (!this.hasValue) { + var err = void 0; + try { + err = this.errorFactory(); + } + catch (e) { + err = e; + } this.destination.error(err); - return; } - this.destination.error(err); - }; - TapSubscriber.prototype._complete = function () { - try { - this._tapComplete.call(this._context); - } - catch (err) { - this.destination.error(err); - return; + else { + return this.destination.complete(); } - return this.destination.complete(); }; - return TapSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=tap.js.map + return ThrowIfEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); +function defaultErrorFactory() { + return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__["EmptyError"](); +} +//# sourceMappingURL=throwIfEmpty.js.map /***/ }), -/* 403 */ +/* 404 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -40953,8 +40944,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "take", function() { return take; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(330); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(311); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(329); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(310); /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ @@ -41007,21 +40998,15 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 404 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return endWith; }); -/* harmony import */ var _observable_fromArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(314); -/* harmony import */ var _observable_scalar__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(316); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(313); -/** PURE_IMPORTS_START _observable_fromArray,_observable_scalar,_observable_empty,_observable_concat,_util_isScheduler PURE_IMPORTS_END */ - - - +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(346); +/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(311); +/** PURE_IMPORTS_START _observable_concat,_observable_of PURE_IMPORTS_END */ function endWith() { @@ -41029,31 +41014,13 @@ function endWith() { for (var _i = 0; _i < arguments.length; _i++) { array[_i] = arguments[_i]; } - return function (source) { - var scheduler = array[array.length - 1]; - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_4__["isScheduler"])(scheduler)) { - array.pop(); - } - else { - scheduler = null; - } - var len = array.length; - if (len === 1 && !scheduler) { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(source, Object(_observable_scalar__WEBPACK_IMPORTED_MODULE_1__["scalar"])(array[0])); - } - else if (len > 0) { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(source, Object(_observable_fromArray__WEBPACK_IMPORTED_MODULE_0__["fromArray"])(array, scheduler)); - } - else { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(source, Object(_observable_empty__WEBPACK_IMPORTED_MODULE_2__["empty"])(scheduler)); - } - }; + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(source, _observable_of__WEBPACK_IMPORTED_MODULE_1__["of"].apply(void 0, array)); }; } //# sourceMappingURL=endWith.js.map /***/ }), -/* 405 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41115,15 +41082,15 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return exhaust; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -41172,18 +41139,20 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return exhaustMap; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(348); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(333); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(350); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ + @@ -41191,20 +41160,20 @@ __webpack_require__.r(__webpack_exports__); function exhaustMap(project, resultSelector) { if (resultSelector) { - return function (source) { return source.pipe(exhaustMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_4__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_3__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; + return function (source) { return source.pipe(exhaustMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; } return function (source) { - return source.lift(new ExhauseMapOperator(project)); + return source.lift(new ExhaustMapOperator(project)); }; } -var ExhauseMapOperator = /*@__PURE__*/ (function () { - function ExhauseMapOperator(project) { +var ExhaustMapOperator = /*@__PURE__*/ (function () { + function ExhaustMapOperator(project) { this.project = project; } - ExhauseMapOperator.prototype.call = function (subscriber, source) { + ExhaustMapOperator.prototype.call = function (subscriber, source) { return source.subscribe(new ExhaustMapSubscriber(subscriber, this.project)); }; - return ExhauseMapOperator; + return ExhaustMapOperator; }()); var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ExhaustMapSubscriber, _super); @@ -41222,22 +41191,30 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { } }; ExhaustMapSubscriber.prototype.tryNext = function (value) { + var result; var index = this.index++; - var destination = this.destination; try { - var result = this.project(value, index); - this.hasSubscription = true; - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, result, value, index)); + result = this.project(value, index); } catch (err) { - destination.error(err); + this.destination.error(err); + return; } + this.hasSubscription = true; + this._innerSub(result, value, index); + }; + ExhaustMapSubscriber.prototype._innerSub = function (result, value, index) { + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, undefined, undefined); + var destination = this.destination; + destination.add(innerSubscriber); + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, value, index, innerSubscriber); }; ExhaustMapSubscriber.prototype._complete = function () { this.hasCompleted = true; if (!this.hasSubscription) { this.destination.complete(); } + this.unsubscribe(); }; ExhaustMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this.destination.next(innerValue); @@ -41246,7 +41223,8 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { this.destination.error(err); }; ExhaustMapSubscriber.prototype.notifyComplete = function (innerSub) { - this.remove(innerSub); + var destination = this.destination; + destination.remove(innerSub); this.hasSubscription = false; if (this.hasCompleted) { this.destination.complete(); @@ -41258,7 +41236,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 408 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41267,13 +41245,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandOperator", function() { return ExpandOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandSubscriber", function() { return ExpandSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -41327,16 +41301,20 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { var index = this.index++; if (this.active < this.concurrent) { destination.next(value); - var result = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_1__["tryCatch"])(this.project)(value, index); - if (result === _util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"]) { - destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"].e); + try { + var project = this.project; + var result = project(value, index); + if (!this.scheduler) { + this.subscribeToProjection(result, value, index); + } + else { + var state = { subscriber: this, result: result, value: value, index: index }; + var destination_1 = this.destination; + destination_1.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); + } } - else if (!this.scheduler) { - this.subscribeToProjection(result, value, index); - } - else { - var state = { subscriber: this, result: result, value: value, index: index }; - this.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); + catch (e) { + destination.error(e); } } else { @@ -41345,20 +41323,23 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { }; ExpandSubscriber.prototype.subscribeToProjection = function (result, value, index) { this.active++; - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, result, value, index)); + var destination = this.destination; + destination.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, result, value, index)); }; ExpandSubscriber.prototype._complete = function () { this.hasCompleted = true; if (this.hasCompleted && this.active === 0) { this.destination.complete(); } + this.unsubscribe(); }; ExpandSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this._next(innerValue); }; ExpandSubscriber.prototype.notifyComplete = function (innerSub) { var buffer = this.buffer; - this.remove(innerSub); + var destination = this.destination; + destination.remove(innerSub); this.active--; if (buffer && buffer.length > 0) { this._next(buffer.shift()); @@ -41368,13 +41349,13 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { } }; return ExpandSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); //# sourceMappingURL=expand.js.map /***/ }), -/* 409 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41412,7 +41393,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 410 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41459,6 +41440,7 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { var destination = this.destination; destination.next(value); destination.complete(); + this.unsubscribe(); }; FindValueSubscriber.prototype._next = function (value) { var _a = this, predicate = _a.predicate, thisArg = _a.thisArg; @@ -41483,13 +41465,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 411 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(410); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(411); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -41499,18 +41481,18 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 412 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(331); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(400); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(403); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(391); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(401); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(328); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(330); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(371); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(404); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(394); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(403); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(327); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -41526,7 +41508,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 413 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41563,7 +41545,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41607,18 +41589,18 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(331); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(400); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(416); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(401); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(391); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(328); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(330); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(371); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(403); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(394); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(327); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -41634,7 +41616,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41642,8 +41624,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return takeLast; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(330); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(311); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(329); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(310); /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ @@ -41711,7 +41693,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41750,7 +41732,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41758,7 +41740,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return materialize; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(310); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(309); /** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -41800,13 +41782,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(420); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -41819,16 +41801,16 @@ function max(comparer) { /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(416); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(391); -/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(292); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(422); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(417); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(394); +/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(291); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -41841,16 +41823,14 @@ function reduce(accumulator, seed) { }; } return function reduceOperatorFunction(source) { - return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(function (acc, value, index) { - return accumulator(acc, value, index + 1); - }), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1))(source); + return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(function (acc, value, index) { return accumulator(acc, value, index + 1); }), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1))(source); }; } //# sourceMappingURL=reduce.js.map /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41932,7 +41912,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41952,13 +41932,13 @@ function merge() { /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return mergeMapTo; }); -/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(356); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(349); /** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ function mergeMapTo(innerObservable, resultSelector, concurrent) { @@ -41977,7 +41957,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -41986,12 +41966,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanOperator", function() { return MergeScanOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanSubscriber", function() { return MergeScanSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(288); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(338); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/** PURE_IMPORTS_START tslib,_util_tryCatch,_util_errorObject,_util_subscribeToResult,_OuterSubscriber PURE_IMPORTS_END */ - +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(338); +/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber PURE_IMPORTS_END */ @@ -42031,22 +42009,27 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { MergeScanSubscriber.prototype._next = function (value) { if (this.active < this.concurrent) { var index = this.index++; - var ish = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_1__["tryCatch"])(this.accumulator)(this.acc, value); var destination = this.destination; - if (ish === _util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"]) { - destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_2__["errorObject"].e); + var ish = void 0; + try { + var accumulator = this.accumulator; + ish = accumulator(this.acc, value, index); } - else { - this.active++; - this._innerSub(ish, value, index); + catch (e) { + return destination.error(e); } + this.active++; + this._innerSub(ish, value, index); } else { this.buffer.push(value); } }; MergeScanSubscriber.prototype._innerSub = function (ish, value, index) { - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, ish, value, index)); + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__["InnerSubscriber"](this, undefined, undefined); + var destination = this.destination; + destination.add(innerSubscriber); + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__["subscribeToResult"])(this, ish, value, index, innerSubscriber); }; MergeScanSubscriber.prototype._complete = function () { this.hasCompleted = true; @@ -42056,6 +42039,7 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { } this.destination.complete(); } + this.unsubscribe(); }; MergeScanSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { var destination = this.destination; @@ -42065,7 +42049,8 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { }; MergeScanSubscriber.prototype.notifyComplete = function (innerSub) { var buffer = this.buffer; - this.remove(innerSub); + var destination = this.destination; + destination.remove(innerSub); this.active--; if (buffer.length > 0) { this._next(buffer.shift()); @@ -42078,19 +42063,19 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { } }; return MergeScanSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); //# sourceMappingURL=mergeScan.js.map /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(420); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -42103,14 +42088,14 @@ function min(comparer) { /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return multicast; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MulticastOperator", function() { return MulticastOperator; }); -/* harmony import */ var _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(294); +/* harmony import */ var _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(293); /** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ function multicast(subjectOrSubjectFactory, selector) { @@ -42152,7 +42137,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -42160,11 +42145,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return onErrorResumeNext; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNextStatic", function() { return onErrorResumeNextStatic; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(348); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(350); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(285); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + @@ -42217,14 +42204,19 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { }; OnErrorResumeNextSubscriber.prototype._error = function (err) { this.subscribeToNextSource(); + this.unsubscribe(); }; OnErrorResumeNextSubscriber.prototype._complete = function () { this.subscribeToNextSource(); + this.unsubscribe(); }; OnErrorResumeNextSubscriber.prototype.subscribeToNextSource = function () { var next = this.nextSources.shift(); - if (next) { - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, next)); + if (!!next) { + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_4__["InnerSubscriber"](this, undefined, undefined); + var destination = this.destination; + destination.add(innerSubscriber); + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, next, undefined, undefined, innerSubscriber); } else { this.destination.complete(); @@ -42236,7 +42228,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -42266,13 +42258,17 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { return _this; } PairwiseSubscriber.prototype._next = function (value) { + var pair; if (this.hasPrev) { - this.destination.next([this.prev, value]); + pair = [this.prev, value]; } else { this.hasPrev = true; } this.prev = value; + if (pair) { + this.destination.next(pair); + } }; return PairwiseSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); @@ -42280,14 +42276,14 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return partition; }); -/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(430); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(400); +/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(370); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(371); /** PURE_IMPORTS_START _util_not,_filter PURE_IMPORTS_END */ @@ -42302,25 +42298,6 @@ function partition(predicate, thisArg) { //# sourceMappingURL=partition.js.map -/***/ }), -/* 430 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "not", function() { return not; }); -/** PURE_IMPORTS_START PURE_IMPORTS_END */ -function not(pred, thisArg) { - function notPred() { - return !(notPred.pred.apply(notPred.thisArg, arguments)); - } - notPred.pred = pred; - notPred.thisArg = thisArg; - return notPred; -} -//# sourceMappingURL=not.js.map - - /***/ }), /* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -42328,7 +42305,7 @@ function not(pred, thisArg) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return pluck; }); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(334); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(333); /** PURE_IMPORTS_START _map PURE_IMPORTS_END */ function pluck() { @@ -42368,8 +42345,8 @@ function plucker(props, length) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(295); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(426); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(294); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -42388,8 +42365,8 @@ function publish(selector) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); -/* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(300); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(426); +/* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(299); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -42406,8 +42383,8 @@ function publishBehavior(value) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); -/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(318); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(426); +/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(317); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -42424,8 +42401,8 @@ function publishLast() { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); -/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(301); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(426); +/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(300); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -42448,7 +42425,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "race", function() { return race; }); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(285); -/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(369); +/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(372); /** PURE_IMPORTS_START _util_isArray,_observable_race PURE_IMPORTS_END */ @@ -42476,7 +42453,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return repeat; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(310); /** PURE_IMPORTS_START tslib,_Subscriber,_observable_empty PURE_IMPORTS_END */ @@ -42540,14 +42517,10 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return repeatWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_Subject,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -42616,15 +42589,19 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { }; RepeatWhenSubscriber.prototype.subscribeToRetries = function () { this.notifications = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - var retries = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.notifier)(this.notifications); - if (retries === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { + var retries; + try { + var notifier = this.notifier; + retries = notifier(this.notifications); + } + catch (e) { return _super.prototype.complete.call(this); } this.retries = retries; - this.retriesSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, retries); + this.retriesSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, retries); }; return RepeatWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); //# sourceMappingURL=repeatWhen.js.map @@ -42689,14 +42666,10 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return retryWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_Subject,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -42729,11 +42702,14 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { var retriesSubscription = this.retriesSubscription; if (!retries) { errors = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - retries = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.notifier)(errors); - if (retries === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - return _super.prototype.error.call(this, _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e); + try { + var notifier = this.notifier; + retries = notifier(errors); } - retriesSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, retries); + catch (e) { + return _super.prototype.error.call(this, e); + } + retriesSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, retries); } else { this.errors = null; @@ -42766,7 +42742,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { this.source.subscribe(this); }; return RetryWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); //# sourceMappingURL=retryWhen.js.map @@ -42778,8 +42754,8 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return sample; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -42836,7 +42812,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return sampleTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(323); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(322); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ @@ -42898,37 +42874,33 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SequenceEqualSubscriber", function() { return SequenceEqualSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_tryCatch,_util_errorObject PURE_IMPORTS_END */ - - +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function sequenceEqual(compareTo, comparor) { - return function (source) { return source.lift(new SequenceEqualOperator(compareTo, comparor)); }; +function sequenceEqual(compareTo, comparator) { + return function (source) { return source.lift(new SequenceEqualOperator(compareTo, comparator)); }; } var SequenceEqualOperator = /*@__PURE__*/ (function () { - function SequenceEqualOperator(compareTo, comparor) { + function SequenceEqualOperator(compareTo, comparator) { this.compareTo = compareTo; - this.comparor = comparor; + this.comparator = comparator; } SequenceEqualOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SequenceEqualSubscriber(subscriber, this.compareTo, this.comparor)); + return source.subscribe(new SequenceEqualSubscriber(subscriber, this.compareTo, this.comparator)); }; return SequenceEqualOperator; }()); var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualSubscriber, _super); - function SequenceEqualSubscriber(destination, compareTo, comparor) { + function SequenceEqualSubscriber(destination, compareTo, comparator) { var _this = _super.call(this, destination) || this; _this.compareTo = compareTo; - _this.comparor = comparor; + _this.comparator = comparator; _this._a = []; _this._b = []; _this._oneComplete = false; - _this.add(compareTo.subscribe(new SequenceEqualCompareToSubscriber(destination, _this))); + _this.destination.add(compareTo.subscribe(new SequenceEqualCompareToSubscriber(destination, _this))); return _this; } SequenceEqualSubscriber.prototype._next = function (value) { @@ -42947,21 +42919,19 @@ var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { else { this._oneComplete = true; } + this.unsubscribe(); }; SequenceEqualSubscriber.prototype.checkValues = function () { - var _c = this, _a = _c._a, _b = _c._b, comparor = _c.comparor; + var _c = this, _a = _c._a, _b = _c._b, comparator = _c.comparator; while (_a.length > 0 && _b.length > 0) { var a = _a.shift(); var b = _b.shift(); var areEqual = false; - if (comparor) { - areEqual = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(comparor)(a, b); - if (areEqual === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - this.destination.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e); - } + try { + areEqual = comparator ? comparator(a, b) : a === b; } - else { - areEqual = a === b; + catch (e) { + this.destination.error(e); } if (!areEqual) { this.emit(false); @@ -42982,6 +42952,14 @@ var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { this.checkValues(); } }; + SequenceEqualSubscriber.prototype.completeB = function () { + if (this._oneComplete) { + this.emit(this._a.length === 0 && this._b.length === 0); + } + else { + this._oneComplete = true; + } + }; return SequenceEqualSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); @@ -42997,9 +42975,11 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { }; SequenceEqualCompareToSubscriber.prototype._error = function (err) { this.parent.error(err); + this.unsubscribe(); }; SequenceEqualCompareToSubscriber.prototype._complete = function () { - this.parent._complete(); + this.parent.completeB(); + this.unsubscribe(); }; return SequenceEqualCompareToSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); @@ -43013,9 +42993,9 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(426); -/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(298); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(295); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(427); +/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(297); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(294); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -43036,13 +43016,26 @@ function share() { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return shareReplay; }); -/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(301); +/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(300); /** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ -function shareReplay(bufferSize, windowTime, scheduler) { - return function (source) { return source.lift(shareReplayOperator(bufferSize, windowTime, scheduler)); }; +function shareReplay(configOrBufferSize, windowTime, scheduler) { + var config; + if (configOrBufferSize && typeof configOrBufferSize === 'object') { + config = configOrBufferSize; + } + else { + config = { + bufferSize: configOrBufferSize, + windowTime: windowTime, + refCount: false, + scheduler: scheduler + }; + } + return function (source) { return source.lift(shareReplayOperator(config)); }; } -function shareReplayOperator(bufferSize, windowTime, scheduler) { +function shareReplayOperator(_a) { + var _b = _a.bufferSize, bufferSize = _b === void 0 ? Number.POSITIVE_INFINITY : _b, _c = _a.windowTime, windowTime = _c === void 0 ? Number.POSITIVE_INFINITY : _c, useRefCount = _a.refCount, scheduler = _a.scheduler; var subject; var refCount = 0; var subscription; @@ -43066,13 +43059,15 @@ function shareReplayOperator(bufferSize, windowTime, scheduler) { }); } var innerSub = subject.subscribe(this); - return function () { + this.add(function () { refCount--; innerSub.unsubscribe(); - if (subscription && refCount === 0 && isComplete) { + if (subscription && !isComplete && useRefCount && refCount === 0) { subscription.unsubscribe(); + subscription = undefined; + subject = undefined; } - }; + }); }; } //# sourceMappingURL=shareReplay.js.map @@ -43087,7 +43082,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "single", function() { return single; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(331); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(330); /** PURE_IMPORTS_START tslib,_Subscriber,_util_EmptyError PURE_IMPORTS_END */ @@ -43209,7 +43204,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return skipLast; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(330); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(329); /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError PURE_IMPORTS_END */ @@ -43270,9 +43265,11 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return skipUntil; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + @@ -43293,7 +43290,10 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { function SkipUntilSubscriber(destination, notifier) { var _this = _super.call(this, destination) || this; _this.hasValue = false; - _this.add(_this.innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, notifier)); + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](_this, undefined, undefined); + _this.add(innerSubscriber); + _this.innerSubscription = innerSubscriber; + Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(_this, notifier, undefined, undefined, innerSubscriber); return _this; } SkipUntilSubscriber.prototype._next = function (value) { @@ -43377,15 +43377,9 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return startWith; }); -/* harmony import */ var _observable_fromArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(314); -/* harmony import */ var _observable_scalar__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(316); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(311); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(313); -/** PURE_IMPORTS_START _observable_fromArray,_observable_scalar,_observable_empty,_observable_concat,_util_isScheduler PURE_IMPORTS_END */ - - - +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(346); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(312); +/** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ function startWith() { @@ -43393,25 +43387,14 @@ function startWith() { for (var _i = 0; _i < arguments.length; _i++) { array[_i] = arguments[_i]; } - return function (source) { - var scheduler = array[array.length - 1]; - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_4__["isScheduler"])(scheduler)) { - array.pop(); - } - else { - scheduler = null; - } - var len = array.length; - if (len === 1 && !scheduler) { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(Object(_observable_scalar__WEBPACK_IMPORTED_MODULE_1__["scalar"])(array[0]), source); - } - else if (len > 0) { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(Object(_observable_fromArray__WEBPACK_IMPORTED_MODULE_0__["fromArray"])(array, scheduler), source); - } - else { - return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_3__["concat"])(Object(_observable_empty__WEBPACK_IMPORTED_MODULE_2__["empty"])(scheduler), source); - } - }; + var scheduler = array[array.length - 1]; + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_1__["isScheduler"])(scheduler)) { + array.pop(); + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source, scheduler); }; + } + else { + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source); }; + } } //# sourceMappingURL=startWith.js.map @@ -43456,7 +43439,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubscribeOnObservable", function() { return SubscribeOnObservable; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(276); -/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(319); +/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(318); /* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(364); /** PURE_IMPORTS_START tslib,_Observable,_scheduler_asap,_util_isNumeric PURE_IMPORTS_END */ @@ -43519,7 +43502,7 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); /* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(455); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(328); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(327); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -43537,11 +43520,13 @@ function switchAll() { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return switchMap; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(348); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(333); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(350); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ + @@ -43549,7 +43534,7 @@ __webpack_require__.r(__webpack_exports__); function switchMap(project, resultSelector) { if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(switchMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_4__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_3__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; + return function (source) { return source.pipe(switchMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; } return function (source) { return source.lift(new SwitchMapOperator(project)); }; } @@ -43587,19 +43572,24 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { if (innerSubscription) { innerSubscription.unsubscribe(); } - this.add(this.innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, result, value, index)); + var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, undefined, undefined); + var destination = this.destination; + destination.add(innerSubscriber); + this.innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, value, index, innerSubscriber); }; SwitchMapSubscriber.prototype._complete = function () { var innerSubscription = this.innerSubscription; if (!innerSubscription || innerSubscription.closed) { _super.prototype._complete.call(this); } + this.unsubscribe(); }; SwitchMapSubscriber.prototype._unsubscribe = function () { this.innerSubscription = null; }; SwitchMapSubscriber.prototype.notifyComplete = function (innerSub) { - this.remove(innerSub); + var destination = this.destination; + destination.remove(innerSub); this.innerSubscription = null; if (this.isStopped) { _super.prototype._complete.call(this); @@ -43637,8 +43627,8 @@ function switchMapTo(innerObservable, resultSelector) { __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return takeUntil; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -43653,7 +43643,7 @@ var TakeUntilOperator = /*@__PURE__*/ (function () { TakeUntilOperator.prototype.call = function (subscriber, source) { var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); var notifierSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(takeUntilSubscriber, this.notifier); - if (notifierSubscription && !notifierSubscription.closed) { + if (notifierSubscription && !takeUntilSubscriber.seenValue) { takeUntilSubscriber.add(notifierSubscription); return source.subscribe(takeUntilSubscriber); } @@ -43664,9 +43654,12 @@ var TakeUntilOperator = /*@__PURE__*/ (function () { var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeUntilSubscriber, _super); function TakeUntilSubscriber(destination) { - return _super.call(this, destination) || this; + var _this = _super.call(this, destination) || this; + _this.seenValue = false; + return _this; } TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + this.seenValue = true; this.complete(); }; TakeUntilSubscriber.prototype.notifyComplete = function () { @@ -43688,23 +43681,30 @@ __webpack_require__.r(__webpack_exports__); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function takeWhile(predicate) { - return function (source) { return source.lift(new TakeWhileOperator(predicate)); }; +function takeWhile(predicate, inclusive) { + if (inclusive === void 0) { + inclusive = false; + } + return function (source) { + return source.lift(new TakeWhileOperator(predicate, inclusive)); + }; } var TakeWhileOperator = /*@__PURE__*/ (function () { - function TakeWhileOperator(predicate) { + function TakeWhileOperator(predicate, inclusive) { this.predicate = predicate; + this.inclusive = inclusive; } TakeWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate)); + return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); }; return TakeWhileOperator; }()); var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeWhileSubscriber, _super); - function TakeWhileSubscriber(destination, predicate) { + function TakeWhileSubscriber(destination, predicate, inclusive) { var _this = _super.call(this, destination) || this; _this.predicate = predicate; + _this.inclusive = inclusive; _this.index = 0; return _this; } @@ -43726,6 +43726,9 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { destination.next(value); } else { + if (this.inclusive) { + destination.next(value); + } destination.complete(); } }; @@ -43738,13 +43741,101 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return tap; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); +/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(292); +/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ + + + + +function tap(nextOrObserver, error, complete) { + return function tapOperatorFunction(source) { + return source.lift(new DoOperator(nextOrObserver, error, complete)); + }; +} +var DoOperator = /*@__PURE__*/ (function () { + function DoOperator(nextOrObserver, error, complete) { + this.nextOrObserver = nextOrObserver; + this.error = error; + this.complete = complete; + } + DoOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); + }; + return DoOperator; +}()); +var TapSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TapSubscriber, _super); + function TapSubscriber(destination, observerOrNext, error, complete) { + var _this = _super.call(this, destination) || this; + _this._tapNext = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_3__["isFunction"])(observerOrNext)) { + _this._context = _this; + _this._tapNext = observerOrNext; + } + else if (observerOrNext) { + _this._context = observerOrNext; + _this._tapNext = observerOrNext.next || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = observerOrNext.error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = observerOrNext.complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + } + return _this; + } + TapSubscriber.prototype._next = function (value) { + try { + this._tapNext.call(this._context, value); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(value); + }; + TapSubscriber.prototype._error = function (err) { + try { + this._tapError.call(this._context, err); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.error(err); + }; + TapSubscriber.prototype._complete = function () { + try { + this._tapComplete.call(this._context); + } + catch (err) { + this.destination.error(err); + return; + } + return this.destination.complete(); + }; + return TapSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=tap.js.map + + +/***/ }), +/* 460 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultThrottleConfig", function() { return defaultThrottleConfig; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return throttle; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -43804,7 +43895,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { }; ThrottleSubscriber.prototype.throttle = function (value) { var duration = this.tryDurationSelector(value); - if (duration) { + if (!!duration) { this.add(this._throttled = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, duration)); } }; @@ -43839,7 +43930,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 460 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -43847,8 +43938,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return throttleTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(323); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(459); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(322); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(460); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -43899,6 +43990,10 @@ var ThrottleTimeSubscriber = /*@__PURE__*/ (function (_super) { if (this.leading) { this.destination.next(value); } + else if (this.trailing) { + this._trailingValue = value; + this._hasTrailingValue = true; + } } }; ThrottleTimeSubscriber.prototype._complete = function () { @@ -43933,17 +44028,17 @@ function dispatchNext(arg) { /***/ }), -/* 461 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(323); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(421); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(322); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(422); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(357); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(334); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -43977,16 +44072,16 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 462 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(323); -/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(332); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(463); -/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(317); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(322); +/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(331); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(464); +/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(316); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -44002,17 +44097,17 @@ function timeout(due, scheduler) { /***/ }), -/* 463 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(323); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(393); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(338); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(322); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(396); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -44084,15 +44179,15 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return timestamp; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Timestamp", function() { return Timestamp; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(323); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(334); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(322); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(333); /** PURE_IMPORTS_START _scheduler_async,_map PURE_IMPORTS_END */ @@ -44114,13 +44209,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 465 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(420); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -44137,16 +44232,16 @@ function toArray() { /***/ }), -/* 466 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "window", function() { return window; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(338); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -44217,7 +44312,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 467 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -44225,7 +44320,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return windowCount; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(295); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(294); /** PURE_IMPORTS_START tslib,_Subscriber,_Subject PURE_IMPORTS_END */ @@ -44307,18 +44402,18 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 468 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return windowTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(323); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(322); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(278); /* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(364); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(313); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(312); /** PURE_IMPORTS_START tslib,_Subject,_scheduler_async,_Subscriber,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ @@ -44477,22 +44572,18 @@ function dispatchWindowClose(state) { /***/ }), -/* 469 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return windowToggle; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(284); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -44518,7 +44609,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { _this.openings = openings; _this.closingSelector = closingSelector; _this.contexts = []; - _this.add(_this.openSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_6__["subscribeToResult"])(_this, openings, openings)); + _this.add(_this.openSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(_this, openings, openings)); return _this; } WindowToggleSubscriber.prototype._next = function (value) { @@ -44573,26 +44664,27 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { }; WindowToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { if (outerValue === this.openings) { - var closingSelector = this.closingSelector; - var closingNotifier = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_3__["tryCatch"])(closingSelector)(innerValue); - if (closingNotifier === _util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"]) { - return this.error(_util_errorObject__WEBPACK_IMPORTED_MODULE_4__["errorObject"].e); + var closingNotifier = void 0; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(innerValue); + } + catch (e) { + return this.error(e); + } + var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](); + var context_4 = { window: window_1, subscription: subscription }; + this.contexts.push(context_4); + var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, closingNotifier, context_4); + if (innerSubscription.closed) { + this.closeWindow(this.contexts.length - 1); } else { - var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](); - var context_4 = { window: window_1, subscription: subscription }; - this.contexts.push(context_4); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_6__["subscribeToResult"])(this, closingNotifier, context_4); - if (innerSubscription.closed) { - this.closeWindow(this.contexts.length - 1); - } - else { - innerSubscription.context = context_4; - subscription.add(innerSubscription); - } - this.destination.next(window_1); + innerSubscription.context = context_4; + subscription.add(innerSubscription); } + this.destination.next(window_1); } else { this.closeWindow(this.contexts.indexOf(outerValue)); @@ -44618,26 +44710,22 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { subscription.unsubscribe(); }; return WindowToggleSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_5__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); //# sourceMappingURL=windowToggle.js.map /***/ }), -/* 470 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return windowWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(295); -/* harmony import */ var _util_tryCatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(287); -/* harmony import */ var _util_errorObject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(288); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(338); -/** PURE_IMPORTS_START tslib,_Subject,_util_tryCatch,_util_errorObject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(294); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(337); +/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -44706,31 +44794,33 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { } var window = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); this.destination.next(window); - var closingNotifier = Object(_util_tryCatch__WEBPACK_IMPORTED_MODULE_2__["tryCatch"])(this.closingSelector)(); - if (closingNotifier === _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"]) { - var err = _util_errorObject__WEBPACK_IMPORTED_MODULE_3__["errorObject"].e; - this.destination.error(err); - this.window.error(err); + var closingNotifier; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(); } - else { - this.add(this.closingNotification = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, closingNotifier)); + catch (e) { + this.destination.error(e); + this.window.error(e); + return; } + this.add(this.closingNotification = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, closingNotifier)); }; return WindowSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); //# sourceMappingURL=windowWhen.js.map /***/ }), -/* 471 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return withLatestFrom; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(279); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(337); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(338); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(336); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(337); /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -44817,13 +44907,13 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return zip; }); -/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(373); +/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(376); /** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ function zip() { @@ -44839,13 +44929,13 @@ function zip() { /***/ }), -/* 473 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return zipAll; }); -/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(373); +/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(376); /** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ function zipAll(project) { @@ -44855,7 +44945,7 @@ function zipAll(project) { /***/ }), -/* 474 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -44865,13 +44955,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var indent_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(259); /* harmony import */ var indent_string__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(indent_string__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(475); +/* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(476); /* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(wrap_ansi__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(172); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(53); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(34); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(36); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(482); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(483); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -44949,13 +45039,13 @@ function toArray(value) { } /***/ }), -/* 475 */ +/* 476 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringWidth = __webpack_require__(476); -const stripAnsi = __webpack_require__(480); +const stringWidth = __webpack_require__(477); +const stripAnsi = __webpack_require__(481); const ESCAPES = new Set([ '\u001B', @@ -45149,13 +45239,13 @@ module.exports = (str, cols, opts) => { /***/ }), -/* 476 */ +/* 477 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stripAnsi = __webpack_require__(477); -const isFullwidthCodePoint = __webpack_require__(479); +const stripAnsi = __webpack_require__(478); +const isFullwidthCodePoint = __webpack_require__(480); module.exports = str => { if (typeof str !== 'string' || str.length === 0) { @@ -45192,18 +45282,18 @@ module.exports = str => { /***/ }), -/* 477 */ +/* 478 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(478); +const ansiRegex = __webpack_require__(479); module.exports = input => typeof input === 'string' ? input.replace(ansiRegex(), '') : input; /***/ }), -/* 478 */ +/* 479 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45220,7 +45310,7 @@ module.exports = () => { /***/ }), -/* 479 */ +/* 480 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45273,18 +45363,18 @@ module.exports = x => { /***/ }), -/* 480 */ +/* 481 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(481); +const ansiRegex = __webpack_require__(482); module.exports = input => typeof input === 'string' ? input.replace(ansiRegex(), '') : input; /***/ }), -/* 481 */ +/* 482 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45301,7 +45391,7 @@ module.exports = () => { /***/ }), -/* 482 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -45454,15 +45544,15 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 483 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(484); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(691); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(692); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -45487,13 +45577,13 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 484 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(485); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(486); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(174); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); @@ -45633,17 +45723,17 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 485 */ +/* 486 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const EventEmitter = __webpack_require__(46); const path = __webpack_require__(16); -const arrify = __webpack_require__(486); -const globby = __webpack_require__(487); -const cpFile = __webpack_require__(681); -const CpyError = __webpack_require__(689); +const arrify = __webpack_require__(487); +const globby = __webpack_require__(488); +const cpFile = __webpack_require__(682); +const CpyError = __webpack_require__(690); const preprocessSrcPath = (srcPath, options) => options.cwd ? path.resolve(options.cwd, srcPath) : srcPath; @@ -45748,7 +45838,7 @@ module.exports.default = cpy; /***/ }), -/* 486 */ +/* 487 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45763,17 +45853,17 @@ module.exports = function (val) { /***/ }), -/* 487 */ +/* 488 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const arrayUnion = __webpack_require__(488); +const arrayUnion = __webpack_require__(489); const glob = __webpack_require__(37); -const fastGlob = __webpack_require__(490); -const dirGlob = __webpack_require__(674); -const gitignore = __webpack_require__(677); +const fastGlob = __webpack_require__(491); +const dirGlob = __webpack_require__(675); +const gitignore = __webpack_require__(678); const DEFAULT_FILTER = () => false; @@ -45918,12 +46008,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 488 */ +/* 489 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(489); +var arrayUniq = __webpack_require__(490); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -45931,7 +46021,7 @@ module.exports = function () { /***/ }), -/* 489 */ +/* 490 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46000,10 +46090,10 @@ if ('Set' in global) { /***/ }), -/* 490 */ +/* 491 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(491); +const pkg = __webpack_require__(492); module.exports = pkg.async; module.exports.default = pkg.async; @@ -46016,19 +46106,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 491 */ +/* 492 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(492); -var taskManager = __webpack_require__(493); -var reader_async_1 = __webpack_require__(645); -var reader_stream_1 = __webpack_require__(669); -var reader_sync_1 = __webpack_require__(670); -var arrayUtils = __webpack_require__(672); -var streamUtils = __webpack_require__(673); +var optionsManager = __webpack_require__(493); +var taskManager = __webpack_require__(494); +var reader_async_1 = __webpack_require__(646); +var reader_stream_1 = __webpack_require__(670); +var reader_sync_1 = __webpack_require__(671); +var arrayUtils = __webpack_require__(673); +var streamUtils = __webpack_require__(674); /** * Synchronous API. */ @@ -46094,7 +46184,7 @@ function isString(source) { /***/ }), -/* 492 */ +/* 493 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46132,13 +46222,13 @@ exports.prepare = prepare; /***/ }), -/* 493 */ +/* 494 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(494); +var patternUtils = __webpack_require__(495); /** * Generate tasks based on parent directory of each pattern. */ @@ -46229,16 +46319,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 494 */ +/* 495 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var globParent = __webpack_require__(495); -var isGlob = __webpack_require__(498); -var micromatch = __webpack_require__(499); +var globParent = __webpack_require__(496); +var isGlob = __webpack_require__(499); +var micromatch = __webpack_require__(500); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -46384,15 +46474,15 @@ exports.matchAny = matchAny; /***/ }), -/* 495 */ +/* 496 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(16); -var isglob = __webpack_require__(496); -var pathDirname = __webpack_require__(497); +var isglob = __webpack_require__(497); +var pathDirname = __webpack_require__(498); var isWin32 = __webpack_require__(11).platform() === 'win32'; module.exports = function globParent(str) { @@ -46415,7 +46505,7 @@ module.exports = function globParent(str) { /***/ }), -/* 496 */ +/* 497 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -46446,7 +46536,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 497 */ +/* 498 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46596,7 +46686,7 @@ module.exports.win32 = win32; /***/ }), -/* 498 */ +/* 499 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -46648,7 +46738,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 499 */ +/* 500 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46659,18 +46749,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(29); -var braces = __webpack_require__(500); -var toRegex = __webpack_require__(603); -var extend = __webpack_require__(611); +var braces = __webpack_require__(501); +var toRegex = __webpack_require__(604); +var extend = __webpack_require__(612); /** * Local dependencies */ -var compilers = __webpack_require__(614); -var parsers = __webpack_require__(641); -var cache = __webpack_require__(642); -var utils = __webpack_require__(643); +var compilers = __webpack_require__(615); +var parsers = __webpack_require__(642); +var cache = __webpack_require__(643); +var utils = __webpack_require__(644); var MAX_LENGTH = 1024 * 64; /** @@ -47532,7 +47622,7 @@ module.exports = micromatch; /***/ }), -/* 500 */ +/* 501 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47542,18 +47632,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(501); -var unique = __webpack_require__(513); -var extend = __webpack_require__(510); +var toRegex = __webpack_require__(502); +var unique = __webpack_require__(514); +var extend = __webpack_require__(511); /** * Local dependencies */ -var compilers = __webpack_require__(514); -var parsers = __webpack_require__(529); -var Braces = __webpack_require__(539); -var utils = __webpack_require__(515); +var compilers = __webpack_require__(515); +var parsers = __webpack_require__(530); +var Braces = __webpack_require__(540); +var utils = __webpack_require__(516); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -47857,15 +47947,15 @@ module.exports = braces; /***/ }), -/* 501 */ +/* 502 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(502); -var extend = __webpack_require__(510); -var not = __webpack_require__(512); +var define = __webpack_require__(503); +var extend = __webpack_require__(511); +var not = __webpack_require__(513); var MAX_LENGTH = 1024 * 64; /** @@ -48012,7 +48102,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 502 */ +/* 503 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48025,7 +48115,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(503); +var isDescriptor = __webpack_require__(504); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -48050,7 +48140,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 503 */ +/* 504 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48063,9 +48153,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(504); -var isAccessor = __webpack_require__(505); -var isData = __webpack_require__(508); +var typeOf = __webpack_require__(505); +var isAccessor = __webpack_require__(506); +var isData = __webpack_require__(509); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -48079,7 +48169,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 504 */ +/* 505 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -48232,7 +48322,7 @@ function isBuffer(val) { /***/ }), -/* 505 */ +/* 506 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48245,7 +48335,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(506); +var typeOf = __webpack_require__(507); // accessor descriptor properties var accessor = { @@ -48308,10 +48398,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 506 */ +/* 507 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(507); +var isBuffer = __webpack_require__(508); var toString = Object.prototype.toString; /** @@ -48430,7 +48520,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 507 */ +/* 508 */ /***/ (function(module, exports) { /*! @@ -48457,7 +48547,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 508 */ +/* 509 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48470,7 +48560,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(509); +var typeOf = __webpack_require__(510); // data descriptor properties var data = { @@ -48519,10 +48609,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 509 */ +/* 510 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(507); +var isBuffer = __webpack_require__(508); var toString = Object.prototype.toString; /** @@ -48641,13 +48731,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 510 */ +/* 511 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(511); +var isObject = __webpack_require__(512); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -48681,7 +48771,7 @@ function hasOwn(obj, key) { /***/ }), -/* 511 */ +/* 512 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48701,13 +48791,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 512 */ +/* 513 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(510); +var extend = __webpack_require__(511); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -48774,7 +48864,7 @@ module.exports = toRegex; /***/ }), -/* 513 */ +/* 514 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48824,13 +48914,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 514 */ +/* 515 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(515); +var utils = __webpack_require__(516); module.exports = function(braces, options) { braces.compiler @@ -49113,25 +49203,25 @@ function hasQueue(node) { /***/ }), -/* 515 */ +/* 516 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(516); +var splitString = __webpack_require__(517); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(510); -utils.flatten = __webpack_require__(522); -utils.isObject = __webpack_require__(520); -utils.fillRange = __webpack_require__(523); -utils.repeat = __webpack_require__(528); -utils.unique = __webpack_require__(513); +utils.extend = __webpack_require__(511); +utils.flatten = __webpack_require__(523); +utils.isObject = __webpack_require__(521); +utils.fillRange = __webpack_require__(524); +utils.repeat = __webpack_require__(529); +utils.unique = __webpack_require__(514); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -49463,7 +49553,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 516 */ +/* 517 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49476,7 +49566,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(517); +var extend = __webpack_require__(518); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -49641,14 +49731,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 517 */ +/* 518 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(518); -var assignSymbols = __webpack_require__(521); +var isExtendable = __webpack_require__(519); +var assignSymbols = __webpack_require__(522); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -49708,7 +49798,7 @@ function isEnum(obj, key) { /***/ }), -/* 518 */ +/* 519 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49721,7 +49811,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(519); +var isPlainObject = __webpack_require__(520); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -49729,7 +49819,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 519 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49742,7 +49832,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(520); +var isObject = __webpack_require__(521); function isObjectObject(o) { return isObject(o) === true @@ -49773,7 +49863,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 520 */ +/* 521 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49792,7 +49882,7 @@ module.exports = function isObject(val) { /***/ }), -/* 521 */ +/* 522 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49839,7 +49929,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 522 */ +/* 523 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49868,7 +49958,7 @@ function flat(arr, res) { /***/ }), -/* 523 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49882,10 +49972,10 @@ function flat(arr, res) { var util = __webpack_require__(29); -var isNumber = __webpack_require__(524); -var extend = __webpack_require__(510); -var repeat = __webpack_require__(526); -var toRegex = __webpack_require__(527); +var isNumber = __webpack_require__(525); +var extend = __webpack_require__(511); +var repeat = __webpack_require__(527); +var toRegex = __webpack_require__(528); /** * Return a range of numbers or letters. @@ -50083,7 +50173,7 @@ module.exports = fillRange; /***/ }), -/* 524 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50096,7 +50186,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(525); +var typeOf = __webpack_require__(526); module.exports = function isNumber(num) { var type = typeOf(num); @@ -50112,10 +50202,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 525 */ +/* 526 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(507); +var isBuffer = __webpack_require__(508); var toString = Object.prototype.toString; /** @@ -50234,7 +50324,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 526 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50311,7 +50401,7 @@ function repeat(str, num) { /***/ }), -/* 527 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50324,8 +50414,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(526); -var isNumber = __webpack_require__(524); +var repeat = __webpack_require__(527); +var isNumber = __webpack_require__(525); var cache = {}; function toRegexRange(min, max, options) { @@ -50612,7 +50702,7 @@ module.exports = toRegexRange; /***/ }), -/* 528 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50637,14 +50727,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 529 */ +/* 530 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(530); -var utils = __webpack_require__(515); +var Node = __webpack_require__(531); +var utils = __webpack_require__(516); /** * Braces parsers @@ -51004,15 +51094,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 530 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(520); -var define = __webpack_require__(531); -var utils = __webpack_require__(538); +var isObject = __webpack_require__(521); +var define = __webpack_require__(532); +var utils = __webpack_require__(539); var ownNames; /** @@ -51503,7 +51593,7 @@ exports = module.exports = Node; /***/ }), -/* 531 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -51516,7 +51606,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(532); +var isDescriptor = __webpack_require__(533); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -51541,7 +51631,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 532 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -51554,9 +51644,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(533); -var isAccessor = __webpack_require__(534); -var isData = __webpack_require__(536); +var typeOf = __webpack_require__(534); +var isAccessor = __webpack_require__(535); +var isData = __webpack_require__(537); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -51569,219 +51659,8 @@ module.exports = function isDescriptor(obj, key) { }; -/***/ }), -/* 533 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; - -module.exports = function kindOf(val) { - if (val === void 0) return 'undefined'; - if (val === null) return 'null'; - - var type = typeof val; - if (type === 'boolean') return 'boolean'; - if (type === 'string') return 'string'; - if (type === 'number') return 'number'; - if (type === 'symbol') return 'symbol'; - if (type === 'function') { - return isGeneratorFn(val) ? 'generatorfunction' : 'function'; - } - - if (isArray(val)) return 'array'; - if (isBuffer(val)) return 'buffer'; - if (isArguments(val)) return 'arguments'; - if (isDate(val)) return 'date'; - if (isError(val)) return 'error'; - if (isRegexp(val)) return 'regexp'; - - switch (ctorName(val)) { - case 'Symbol': return 'symbol'; - case 'Promise': return 'promise'; - - // Set, Map, WeakSet, WeakMap - case 'WeakMap': return 'weakmap'; - case 'WeakSet': return 'weakset'; - case 'Map': return 'map'; - case 'Set': return 'set'; - - // 8-bit typed arrays - case 'Int8Array': return 'int8array'; - case 'Uint8Array': return 'uint8array'; - case 'Uint8ClampedArray': return 'uint8clampedarray'; - - // 16-bit typed arrays - case 'Int16Array': return 'int16array'; - case 'Uint16Array': return 'uint16array'; - - // 32-bit typed arrays - case 'Int32Array': return 'int32array'; - case 'Uint32Array': return 'uint32array'; - case 'Float32Array': return 'float32array'; - case 'Float64Array': return 'float64array'; - } - - if (isGeneratorObj(val)) { - return 'generator'; - } - - // Non-plain objects - type = toString.call(val); - switch (type) { - case '[object Object]': return 'object'; - // iterators - case '[object Map Iterator]': return 'mapiterator'; - case '[object Set Iterator]': return 'setiterator'; - case '[object String Iterator]': return 'stringiterator'; - case '[object Array Iterator]': return 'arrayiterator'; - } - - // other - return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); -}; - -function ctorName(val) { - return val.constructor ? val.constructor.name : null; -} - -function isArray(val) { - if (Array.isArray) return Array.isArray(val); - return val instanceof Array; -} - -function isError(val) { - return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); -} - -function isDate(val) { - if (val instanceof Date) return true; - return typeof val.toDateString === 'function' - && typeof val.getDate === 'function' - && typeof val.setDate === 'function'; -} - -function isRegexp(val) { - if (val instanceof RegExp) return true; - return typeof val.flags === 'string' - && typeof val.ignoreCase === 'boolean' - && typeof val.multiline === 'boolean' - && typeof val.global === 'boolean'; -} - -function isGeneratorFn(name, val) { - return ctorName(name) === 'GeneratorFunction'; -} - -function isGeneratorObj(val) { - return typeof val.throw === 'function' - && typeof val.return === 'function' - && typeof val.next === 'function'; -} - -function isArguments(val) { - try { - if (typeof val.length === 'number' && typeof val.callee === 'function') { - return true; - } - } catch (err) { - if (err.message.indexOf('callee') !== -1) { - return true; - } - } - return false; -} - -/** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer - */ - -function isBuffer(val) { - if (val.constructor && typeof val.constructor.isBuffer === 'function') { - return val.constructor.isBuffer(val); - } - return false; -} - - /***/ }), /* 534 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-accessor-descriptor - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var typeOf = __webpack_require__(535); - -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' -}; - -function isAccessorDescriptor(obj, prop) { - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } - - if (typeOf(obj) !== 'object') { - return false; - } - - if (has(obj, 'value') || has(obj, 'writable')) { - return false; - } - - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; - } - - // tldr: it's valid to have "set" be undefined - // "set" might be undefined if `Object.getOwnPropertyDescriptor` - // was used to get the value, and only `get` was defined by the user - if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { - return false; - } - - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === accessor[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } - } - return true; -} - -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} - -/** - * Expose `isAccessorDescriptor` - */ - -module.exports = isAccessorDescriptor; - - -/***/ }), -/* 535 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -51915,8 +51794,219 @@ function isBuffer(val) { } +/***/ }), +/* 535 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-accessor-descriptor + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. + */ + + + +var typeOf = __webpack_require__(536); + +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' +}; + +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } + + if (typeOf(obj) !== 'object') { + return false; + } + + if (has(obj, 'value') || has(obj, 'writable')) { + return false; + } + + if (!has(obj, 'get') || typeof obj.get !== 'function') { + return false; + } + + // tldr: it's valid to have "set" be undefined + // "set" might be undefined if `Object.getOwnPropertyDescriptor` + // was used to get the value, and only `get` was defined by the user + if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { + return false; + } + + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; + } + + if (typeOf(obj[key]) === accessor[key]) { + continue; + } + + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; +} + +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); +} + +/** + * Expose `isAccessorDescriptor` + */ + +module.exports = isAccessorDescriptor; + + /***/ }), /* 536 */ +/***/ (function(module, exports) { + +var toString = Object.prototype.toString; + +module.exports = function kindOf(val) { + if (val === void 0) return 'undefined'; + if (val === null) return 'null'; + + var type = typeof val; + if (type === 'boolean') return 'boolean'; + if (type === 'string') return 'string'; + if (type === 'number') return 'number'; + if (type === 'symbol') return 'symbol'; + if (type === 'function') { + return isGeneratorFn(val) ? 'generatorfunction' : 'function'; + } + + if (isArray(val)) return 'array'; + if (isBuffer(val)) return 'buffer'; + if (isArguments(val)) return 'arguments'; + if (isDate(val)) return 'date'; + if (isError(val)) return 'error'; + if (isRegexp(val)) return 'regexp'; + + switch (ctorName(val)) { + case 'Symbol': return 'symbol'; + case 'Promise': return 'promise'; + + // Set, Map, WeakSet, WeakMap + case 'WeakMap': return 'weakmap'; + case 'WeakSet': return 'weakset'; + case 'Map': return 'map'; + case 'Set': return 'set'; + + // 8-bit typed arrays + case 'Int8Array': return 'int8array'; + case 'Uint8Array': return 'uint8array'; + case 'Uint8ClampedArray': return 'uint8clampedarray'; + + // 16-bit typed arrays + case 'Int16Array': return 'int16array'; + case 'Uint16Array': return 'uint16array'; + + // 32-bit typed arrays + case 'Int32Array': return 'int32array'; + case 'Uint32Array': return 'uint32array'; + case 'Float32Array': return 'float32array'; + case 'Float64Array': return 'float64array'; + } + + if (isGeneratorObj(val)) { + return 'generator'; + } + + // Non-plain objects + type = toString.call(val); + switch (type) { + case '[object Object]': return 'object'; + // iterators + case '[object Map Iterator]': return 'mapiterator'; + case '[object Set Iterator]': return 'setiterator'; + case '[object String Iterator]': return 'stringiterator'; + case '[object Array Iterator]': return 'arrayiterator'; + } + + // other + return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); +}; + +function ctorName(val) { + return val.constructor ? val.constructor.name : null; +} + +function isArray(val) { + if (Array.isArray) return Array.isArray(val); + return val instanceof Array; +} + +function isError(val) { + return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); +} + +function isDate(val) { + if (val instanceof Date) return true; + return typeof val.toDateString === 'function' + && typeof val.getDate === 'function' + && typeof val.setDate === 'function'; +} + +function isRegexp(val) { + if (val instanceof RegExp) return true; + return typeof val.flags === 'string' + && typeof val.ignoreCase === 'boolean' + && typeof val.multiline === 'boolean' + && typeof val.global === 'boolean'; +} + +function isGeneratorFn(name, val) { + return ctorName(name) === 'GeneratorFunction'; +} + +function isGeneratorObj(val) { + return typeof val.throw === 'function' + && typeof val.return === 'function' + && typeof val.next === 'function'; +} + +function isArguments(val) { + try { + if (typeof val.length === 'number' && typeof val.callee === 'function') { + return true; + } + } catch (err) { + if (err.message.indexOf('callee') !== -1) { + return true; + } + } + return false; +} + +/** + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer + */ + +function isBuffer(val) { + if (val.constructor && typeof val.constructor.isBuffer === 'function') { + return val.constructor.isBuffer(val); + } + return false; +} + + +/***/ }), +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -51929,7 +52019,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(537); +var typeOf = __webpack_require__(538); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -51972,7 +52062,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 537 */ +/* 538 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -52107,13 +52197,13 @@ function isBuffer(val) { /***/ }), -/* 538 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(525); +var typeOf = __webpack_require__(526); var utils = module.exports; /** @@ -53133,17 +53223,17 @@ function assert(val, message) { /***/ }), -/* 539 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(510); -var Snapdragon = __webpack_require__(540); -var compilers = __webpack_require__(514); -var parsers = __webpack_require__(529); -var utils = __webpack_require__(515); +var extend = __webpack_require__(511); +var Snapdragon = __webpack_require__(541); +var compilers = __webpack_require__(515); +var parsers = __webpack_require__(530); +var utils = __webpack_require__(516); /** * Customize Snapdragon parser and renderer @@ -53244,17 +53334,17 @@ module.exports = Braces; /***/ }), -/* 540 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(541); -var define = __webpack_require__(502); -var Compiler = __webpack_require__(570); -var Parser = __webpack_require__(600); -var utils = __webpack_require__(580); +var Base = __webpack_require__(542); +var define = __webpack_require__(503); +var Compiler = __webpack_require__(571); +var Parser = __webpack_require__(601); +var utils = __webpack_require__(581); var regexCache = {}; var cache = {}; @@ -53425,20 +53515,20 @@ module.exports.Parser = Parser; /***/ }), -/* 541 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var define = __webpack_require__(542); -var CacheBase = __webpack_require__(543); -var Emitter = __webpack_require__(544); -var isObject = __webpack_require__(520); -var merge = __webpack_require__(561); -var pascal = __webpack_require__(564); -var cu = __webpack_require__(565); +var define = __webpack_require__(543); +var CacheBase = __webpack_require__(544); +var Emitter = __webpack_require__(545); +var isObject = __webpack_require__(521); +var merge = __webpack_require__(562); +var pascal = __webpack_require__(565); +var cu = __webpack_require__(566); /** * Optionally define a custom `cache` namespace to use. @@ -53867,7 +53957,7 @@ module.exports.namespace = namespace; /***/ }), -/* 542 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -53880,7 +53970,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(532); +var isDescriptor = __webpack_require__(533); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -53905,21 +53995,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 543 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(520); -var Emitter = __webpack_require__(544); -var visit = __webpack_require__(545); -var toPath = __webpack_require__(548); -var union = __webpack_require__(549); -var del = __webpack_require__(553); -var get = __webpack_require__(551); -var has = __webpack_require__(558); -var set = __webpack_require__(552); +var isObject = __webpack_require__(521); +var Emitter = __webpack_require__(545); +var visit = __webpack_require__(546); +var toPath = __webpack_require__(549); +var union = __webpack_require__(550); +var del = __webpack_require__(554); +var get = __webpack_require__(552); +var has = __webpack_require__(559); +var set = __webpack_require__(553); /** * Create a `Cache` constructor that when instantiated will @@ -54173,7 +54263,7 @@ module.exports.namespace = namespace; /***/ }), -/* 544 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { @@ -54342,7 +54432,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 545 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54355,8 +54445,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(546); -var mapVisit = __webpack_require__(547); +var visit = __webpack_require__(547); +var mapVisit = __webpack_require__(548); module.exports = function(collection, method, val) { var result; @@ -54379,7 +54469,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 546 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54392,7 +54482,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(520); +var isObject = __webpack_require__(521); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -54419,14 +54509,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 547 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var visit = __webpack_require__(546); +var visit = __webpack_require__(547); /** * Map `visit` over an array of objects. @@ -54463,7 +54553,7 @@ function isObject(val) { /***/ }), -/* 548 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54476,7 +54566,7 @@ function isObject(val) { -var typeOf = __webpack_require__(525); +var typeOf = __webpack_require__(526); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -54503,16 +54593,16 @@ function filter(arr) { /***/ }), -/* 549 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(511); -var union = __webpack_require__(550); -var get = __webpack_require__(551); -var set = __webpack_require__(552); +var isObject = __webpack_require__(512); +var union = __webpack_require__(551); +var get = __webpack_require__(552); +var set = __webpack_require__(553); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -54540,7 +54630,7 @@ function arrayify(val) { /***/ }), -/* 550 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54576,7 +54666,7 @@ module.exports = function union(init) { /***/ }), -/* 551 */ +/* 552 */ /***/ (function(module, exports) { /*! @@ -54632,7 +54722,7 @@ function toString(val) { /***/ }), -/* 552 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54645,10 +54735,10 @@ function toString(val) { -var split = __webpack_require__(516); -var extend = __webpack_require__(510); -var isPlainObject = __webpack_require__(519); -var isObject = __webpack_require__(511); +var split = __webpack_require__(517); +var extend = __webpack_require__(511); +var isPlainObject = __webpack_require__(520); +var isObject = __webpack_require__(512); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -54694,7 +54784,7 @@ function isValidKey(key) { /***/ }), -/* 553 */ +/* 554 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54707,8 +54797,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(520); -var has = __webpack_require__(554); +var isObject = __webpack_require__(521); +var has = __webpack_require__(555); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -54733,7 +54823,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 554 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54746,9 +54836,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(555); -var hasValues = __webpack_require__(557); -var get = __webpack_require__(551); +var isObject = __webpack_require__(556); +var hasValues = __webpack_require__(558); +var get = __webpack_require__(552); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -54759,7 +54849,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 555 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54772,7 +54862,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(556); +var isArray = __webpack_require__(557); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -54780,7 +54870,7 @@ module.exports = function isObject(val) { /***/ }), -/* 556 */ +/* 557 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -54791,7 +54881,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 557 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54834,7 +54924,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 558 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54847,9 +54937,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(520); -var hasValues = __webpack_require__(559); -var get = __webpack_require__(551); +var isObject = __webpack_require__(521); +var hasValues = __webpack_require__(560); +var get = __webpack_require__(552); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -54857,7 +54947,7 @@ module.exports = function(val, prop) { /***/ }), -/* 559 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -54870,8 +54960,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(560); -var isNumber = __webpack_require__(524); +var typeOf = __webpack_require__(561); +var isNumber = __webpack_require__(525); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -54924,10 +55014,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 560 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(507); +var isBuffer = __webpack_require__(508); var toString = Object.prototype.toString; /** @@ -55049,14 +55139,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 561 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(562); -var forIn = __webpack_require__(563); +var isExtendable = __webpack_require__(563); +var forIn = __webpack_require__(564); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -55120,7 +55210,7 @@ module.exports = mixinDeep; /***/ }), -/* 562 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55133,7 +55223,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(519); +var isPlainObject = __webpack_require__(520); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -55141,7 +55231,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 563 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55164,7 +55254,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 564 */ +/* 565 */ /***/ (function(module, exports) { /*! @@ -55191,14 +55281,14 @@ module.exports = pascalcase; /***/ }), -/* 565 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var utils = __webpack_require__(566); +var utils = __webpack_require__(567); /** * Expose class utils @@ -55563,7 +55653,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 566 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55577,10 +55667,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(550); -utils.define = __webpack_require__(502); -utils.isObj = __webpack_require__(520); -utils.staticExtend = __webpack_require__(567); +utils.union = __webpack_require__(551); +utils.define = __webpack_require__(503); +utils.isObj = __webpack_require__(521); +utils.staticExtend = __webpack_require__(568); /** @@ -55591,7 +55681,7 @@ module.exports = utils; /***/ }), -/* 567 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55604,8 +55694,8 @@ module.exports = utils; -var copy = __webpack_require__(568); -var define = __webpack_require__(502); +var copy = __webpack_require__(569); +var define = __webpack_require__(503); var util = __webpack_require__(29); /** @@ -55688,15 +55778,15 @@ module.exports = extend; /***/ }), -/* 568 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(525); -var copyDescriptor = __webpack_require__(569); -var define = __webpack_require__(502); +var typeOf = __webpack_require__(526); +var copyDescriptor = __webpack_require__(570); +var define = __webpack_require__(503); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -55869,7 +55959,7 @@ module.exports.has = has; /***/ }), -/* 569 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55957,16 +56047,16 @@ function isObject(val) { /***/ }), -/* 570 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(571); -var define = __webpack_require__(502); -var debug = __webpack_require__(573)('snapdragon:compiler'); -var utils = __webpack_require__(580); +var use = __webpack_require__(572); +var define = __webpack_require__(503); +var debug = __webpack_require__(574)('snapdragon:compiler'); +var utils = __webpack_require__(581); /** * Create a new `Compiler` with the given `options`. @@ -56120,7 +56210,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(599); + var sourcemaps = __webpack_require__(600); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -56141,7 +56231,7 @@ module.exports = Compiler; /***/ }), -/* 571 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56154,7 +56244,7 @@ module.exports = Compiler; -var utils = __webpack_require__(572); +var utils = __webpack_require__(573); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -56269,7 +56359,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 572 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56283,8 +56373,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(502); -utils.isObject = __webpack_require__(520); +utils.define = __webpack_require__(503); +utils.isObject = __webpack_require__(521); utils.isString = function(val) { @@ -56299,7 +56389,7 @@ module.exports = utils; /***/ }), -/* 573 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -56308,14 +56398,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(574); + module.exports = __webpack_require__(575); } else { - module.exports = __webpack_require__(577); + module.exports = __webpack_require__(578); } /***/ }), -/* 574 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -56324,7 +56414,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(575); +exports = module.exports = __webpack_require__(576); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -56506,7 +56596,7 @@ function localstorage() { /***/ }), -/* 575 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { @@ -56522,7 +56612,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(576); +exports.humanize = __webpack_require__(577); /** * The currently active debug mode names, and names to skip. @@ -56714,7 +56804,7 @@ function coerce(val) { /***/ }), -/* 576 */ +/* 577 */ /***/ (function(module, exports) { /** @@ -56872,14 +56962,14 @@ function plural(ms, n, name) { /***/ }), -/* 577 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ -var tty = __webpack_require__(578); +var tty = __webpack_require__(579); var util = __webpack_require__(29); /** @@ -56888,7 +56978,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(575); +exports = module.exports = __webpack_require__(576); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -57067,7 +57157,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(579); + var net = __webpack_require__(580); stream = new net.Socket({ fd: fd, readable: false, @@ -57126,19 +57216,19 @@ exports.enable(load()); /***/ }), -/* 578 */ +/* 579 */ /***/ (function(module, exports) { module.exports = require("tty"); /***/ }), -/* 579 */ +/* 580 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 580 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57148,9 +57238,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(510); -exports.SourceMap = __webpack_require__(581); -exports.sourceMapResolve = __webpack_require__(592); +exports.extend = __webpack_require__(511); +exports.SourceMap = __webpack_require__(582); +exports.sourceMapResolve = __webpack_require__(593); /** * Convert backslash in the given string to forward slashes @@ -57193,7 +57283,7 @@ exports.last = function(arr, n) { /***/ }), -/* 581 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -57201,13 +57291,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(582).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(588).SourceMapConsumer; -exports.SourceNode = __webpack_require__(591).SourceNode; +exports.SourceMapGenerator = __webpack_require__(583).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(589).SourceMapConsumer; +exports.SourceNode = __webpack_require__(592).SourceNode; /***/ }), -/* 582 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -57217,10 +57307,10 @@ exports.SourceNode = __webpack_require__(591).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(583); -var util = __webpack_require__(585); -var ArraySet = __webpack_require__(586).ArraySet; -var MappingList = __webpack_require__(587).MappingList; +var base64VLQ = __webpack_require__(584); +var util = __webpack_require__(586); +var ArraySet = __webpack_require__(587).ArraySet; +var MappingList = __webpack_require__(588).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -57629,7 +57719,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 583 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -57669,7 +57759,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(584); +var base64 = __webpack_require__(585); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -57775,7 +57865,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 584 */ +/* 585 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -57848,7 +57938,7 @@ exports.decode = function (charCode) { /***/ }), -/* 585 */ +/* 586 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -58271,7 +58361,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 586 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -58281,7 +58371,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(585); +var util = __webpack_require__(586); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -58398,7 +58488,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 587 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -58408,7 +58498,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(585); +var util = __webpack_require__(586); /** * Determine whether mappingB is after mappingA with respect to generated @@ -58483,7 +58573,7 @@ exports.MappingList = MappingList; /***/ }), -/* 588 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -58493,11 +58583,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(585); -var binarySearch = __webpack_require__(589); -var ArraySet = __webpack_require__(586).ArraySet; -var base64VLQ = __webpack_require__(583); -var quickSort = __webpack_require__(590).quickSort; +var util = __webpack_require__(586); +var binarySearch = __webpack_require__(590); +var ArraySet = __webpack_require__(587).ArraySet; +var base64VLQ = __webpack_require__(584); +var quickSort = __webpack_require__(591).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -59571,7 +59661,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 589 */ +/* 590 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -59688,7 +59778,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 590 */ +/* 591 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -59808,7 +59898,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 591 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -59818,8 +59908,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(582).SourceMapGenerator; -var util = __webpack_require__(585); +var SourceMapGenerator = __webpack_require__(583).SourceMapGenerator; +var util = __webpack_require__(586); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -60227,17 +60317,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 592 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(593) -var resolveUrl = __webpack_require__(594) -var decodeUriComponent = __webpack_require__(595) -var urix = __webpack_require__(597) -var atob = __webpack_require__(598) +var sourceMappingURL = __webpack_require__(594) +var resolveUrl = __webpack_require__(595) +var decodeUriComponent = __webpack_require__(596) +var urix = __webpack_require__(598) +var atob = __webpack_require__(599) @@ -60535,7 +60625,7 @@ module.exports = { /***/ }), -/* 593 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -60598,7 +60688,7 @@ void (function(root, factory) { /***/ }), -/* 594 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -60616,13 +60706,13 @@ module.exports = resolveUrl /***/ }), -/* 595 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(596) +var decodeUriComponent = __webpack_require__(597) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -60633,7 +60723,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 596 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60734,7 +60824,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 597 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -60757,7 +60847,7 @@ module.exports = urix /***/ }), -/* 598 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60771,7 +60861,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 599 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60779,8 +60869,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(23); var path = __webpack_require__(16); -var define = __webpack_require__(502); -var utils = __webpack_require__(580); +var define = __webpack_require__(503); +var utils = __webpack_require__(581); /** * Expose `mixin()`. @@ -60923,19 +61013,19 @@ exports.comment = function(node) { /***/ }), -/* 600 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(571); +var use = __webpack_require__(572); var util = __webpack_require__(29); -var Cache = __webpack_require__(601); -var define = __webpack_require__(502); -var debug = __webpack_require__(573)('snapdragon:parser'); -var Position = __webpack_require__(602); -var utils = __webpack_require__(580); +var Cache = __webpack_require__(602); +var define = __webpack_require__(503); +var debug = __webpack_require__(574)('snapdragon:parser'); +var Position = __webpack_require__(603); +var utils = __webpack_require__(581); /** * Create a new `Parser` with the given `input` and `options`. @@ -61463,7 +61553,7 @@ module.exports = Parser; /***/ }), -/* 601 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61570,13 +61660,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 602 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(502); +var define = __webpack_require__(503); /** * Store position for a node @@ -61591,16 +61681,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 603 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(604); -var define = __webpack_require__(610); -var extend = __webpack_require__(611); -var not = __webpack_require__(613); +var safe = __webpack_require__(605); +var define = __webpack_require__(611); +var extend = __webpack_require__(612); +var not = __webpack_require__(614); var MAX_LENGTH = 1024 * 64; /** @@ -61753,10 +61843,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 604 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(605); +var parse = __webpack_require__(606); var types = parse.types; module.exports = function (re, opts) { @@ -61802,13 +61892,13 @@ function isRegExp (x) { /***/ }), -/* 605 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(606); -var types = __webpack_require__(607); -var sets = __webpack_require__(608); -var positions = __webpack_require__(609); +var util = __webpack_require__(607); +var types = __webpack_require__(608); +var sets = __webpack_require__(609); +var positions = __webpack_require__(610); module.exports = function(regexpStr) { @@ -62090,11 +62180,11 @@ module.exports.types = types; /***/ }), -/* 606 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(607); -var sets = __webpack_require__(608); +var types = __webpack_require__(608); +var sets = __webpack_require__(609); // All of these are private and only used by randexp. @@ -62207,7 +62297,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 607 */ +/* 608 */ /***/ (function(module, exports) { module.exports = { @@ -62223,10 +62313,10 @@ module.exports = { /***/ }), -/* 608 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(607); +var types = __webpack_require__(608); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -62311,10 +62401,10 @@ exports.anyChar = function() { /***/ }), -/* 609 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(607); +var types = __webpack_require__(608); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -62334,7 +62424,7 @@ exports.end = function() { /***/ }), -/* 610 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62347,8 +62437,8 @@ exports.end = function() { -var isobject = __webpack_require__(520); -var isDescriptor = __webpack_require__(532); +var isobject = __webpack_require__(521); +var isDescriptor = __webpack_require__(533); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -62379,14 +62469,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 611 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(612); -var assignSymbols = __webpack_require__(521); +var isExtendable = __webpack_require__(613); +var assignSymbols = __webpack_require__(522); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -62446,7 +62536,7 @@ function isEnum(obj, key) { /***/ }), -/* 612 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62459,7 +62549,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(519); +var isPlainObject = __webpack_require__(520); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -62467,14 +62557,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 613 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(611); -var safe = __webpack_require__(604); +var extend = __webpack_require__(612); +var safe = __webpack_require__(605); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -62546,14 +62636,14 @@ module.exports = toRegex; /***/ }), -/* 614 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(615); -var extglob = __webpack_require__(630); +var nanomatch = __webpack_require__(616); +var extglob = __webpack_require__(631); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -62630,7 +62720,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 615 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62641,17 +62731,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(29); -var toRegex = __webpack_require__(501); -var extend = __webpack_require__(616); +var toRegex = __webpack_require__(502); +var extend = __webpack_require__(617); /** * Local dependencies */ -var compilers = __webpack_require__(618); -var parsers = __webpack_require__(619); -var cache = __webpack_require__(622); -var utils = __webpack_require__(624); +var compilers = __webpack_require__(619); +var parsers = __webpack_require__(620); +var cache = __webpack_require__(623); +var utils = __webpack_require__(625); var MAX_LENGTH = 1024 * 64; /** @@ -63475,14 +63565,14 @@ module.exports = nanomatch; /***/ }), -/* 616 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(617); -var assignSymbols = __webpack_require__(521); +var isExtendable = __webpack_require__(618); +var assignSymbols = __webpack_require__(522); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -63542,7 +63632,7 @@ function isEnum(obj, key) { /***/ }), -/* 617 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63555,7 +63645,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(519); +var isPlainObject = __webpack_require__(520); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -63563,7 +63653,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 618 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63909,15 +63999,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 619 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(512); -var toRegex = __webpack_require__(501); -var isOdd = __webpack_require__(620); +var regexNot = __webpack_require__(513); +var toRegex = __webpack_require__(502); +var isOdd = __webpack_require__(621); /** * Characters to use in negation regex (we want to "not" match @@ -64303,7 +64393,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 620 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64316,7 +64406,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(621); +var isNumber = __webpack_require__(622); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -64330,7 +64420,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 621 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64358,14 +64448,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 622 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(623))(); +module.exports = new (__webpack_require__(624))(); /***/ }), -/* 623 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64378,7 +64468,7 @@ module.exports = new (__webpack_require__(623))(); -var MapCache = __webpack_require__(601); +var MapCache = __webpack_require__(602); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -64500,7 +64590,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 624 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64513,14 +64603,14 @@ var path = __webpack_require__(16); * Module dependencies */ -var isWindows = __webpack_require__(625)(); -var Snapdragon = __webpack_require__(540); -utils.define = __webpack_require__(626); -utils.diff = __webpack_require__(627); -utils.extend = __webpack_require__(616); -utils.pick = __webpack_require__(628); -utils.typeOf = __webpack_require__(629); -utils.unique = __webpack_require__(513); +var isWindows = __webpack_require__(626)(); +var Snapdragon = __webpack_require__(541); +utils.define = __webpack_require__(627); +utils.diff = __webpack_require__(628); +utils.extend = __webpack_require__(617); +utils.pick = __webpack_require__(629); +utils.typeOf = __webpack_require__(630); +utils.unique = __webpack_require__(514); /** * Returns true if the given value is effectively an empty string @@ -64886,7 +64976,7 @@ utils.unixify = function(options) { /***/ }), -/* 625 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -64914,7 +65004,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 626 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64927,8 +65017,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(520); -var isDescriptor = __webpack_require__(532); +var isobject = __webpack_require__(521); +var isDescriptor = __webpack_require__(533); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -64959,7 +65049,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 627 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65013,7 +65103,7 @@ function diffArray(one, two) { /***/ }), -/* 628 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65026,7 +65116,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(520); +var isObject = __webpack_require__(521); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -65055,7 +65145,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 629 */ +/* 630 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -65190,7 +65280,7 @@ function isBuffer(val) { /***/ }), -/* 630 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65200,18 +65290,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(510); -var unique = __webpack_require__(513); -var toRegex = __webpack_require__(501); +var extend = __webpack_require__(511); +var unique = __webpack_require__(514); +var toRegex = __webpack_require__(502); /** * Local dependencies */ -var compilers = __webpack_require__(631); -var parsers = __webpack_require__(637); -var Extglob = __webpack_require__(640); -var utils = __webpack_require__(639); +var compilers = __webpack_require__(632); +var parsers = __webpack_require__(638); +var Extglob = __webpack_require__(641); +var utils = __webpack_require__(640); var MAX_LENGTH = 1024 * 64; /** @@ -65528,13 +65618,13 @@ module.exports = extglob; /***/ }), -/* 631 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(632); +var brackets = __webpack_require__(633); /** * Extglob compilers @@ -65704,7 +65794,7 @@ module.exports = function(extglob) { /***/ }), -/* 632 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65714,17 +65804,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(633); -var parsers = __webpack_require__(635); +var compilers = __webpack_require__(634); +var parsers = __webpack_require__(636); /** * Module dependencies */ -var debug = __webpack_require__(573)('expand-brackets'); -var extend = __webpack_require__(510); -var Snapdragon = __webpack_require__(540); -var toRegex = __webpack_require__(501); +var debug = __webpack_require__(574)('expand-brackets'); +var extend = __webpack_require__(511); +var Snapdragon = __webpack_require__(541); +var toRegex = __webpack_require__(502); /** * Parses the given POSIX character class `pattern` and returns a @@ -65922,13 +66012,13 @@ module.exports = brackets; /***/ }), -/* 633 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(634); +var posix = __webpack_require__(635); module.exports = function(brackets) { brackets.compiler @@ -66016,7 +66106,7 @@ module.exports = function(brackets) { /***/ }), -/* 634 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66045,14 +66135,14 @@ module.exports = { /***/ }), -/* 635 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(636); -var define = __webpack_require__(502); +var utils = __webpack_require__(637); +var define = __webpack_require__(503); /** * Text regex @@ -66271,14 +66361,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 636 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(501); -var regexNot = __webpack_require__(512); +var toRegex = __webpack_require__(502); +var regexNot = __webpack_require__(513); var cached; /** @@ -66312,15 +66402,15 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 637 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(632); -var define = __webpack_require__(638); -var utils = __webpack_require__(639); +var brackets = __webpack_require__(633); +var define = __webpack_require__(639); +var utils = __webpack_require__(640); /** * Characters to use in text regex (we want to "not" match @@ -66475,7 +66565,7 @@ module.exports = parsers; /***/ }), -/* 638 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66488,7 +66578,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(532); +var isDescriptor = __webpack_require__(533); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -66513,14 +66603,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 639 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(512); -var Cache = __webpack_require__(623); +var regex = __webpack_require__(513); +var Cache = __webpack_require__(624); /** * Utils @@ -66589,7 +66679,7 @@ utils.createRegex = function(str) { /***/ }), -/* 640 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66599,16 +66689,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(540); -var define = __webpack_require__(638); -var extend = __webpack_require__(510); +var Snapdragon = __webpack_require__(541); +var define = __webpack_require__(639); +var extend = __webpack_require__(511); /** * Local dependencies */ -var compilers = __webpack_require__(631); -var parsers = __webpack_require__(637); +var compilers = __webpack_require__(632); +var parsers = __webpack_require__(638); /** * Customize Snapdragon parser and renderer @@ -66674,16 +66764,16 @@ module.exports = Extglob; /***/ }), -/* 641 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(630); -var nanomatch = __webpack_require__(615); -var regexNot = __webpack_require__(512); -var toRegex = __webpack_require__(603); +var extglob = __webpack_require__(631); +var nanomatch = __webpack_require__(616); +var regexNot = __webpack_require__(513); +var toRegex = __webpack_require__(604); var not; /** @@ -66764,14 +66854,14 @@ function textRegex(pattern) { /***/ }), -/* 642 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(623))(); +module.exports = new (__webpack_require__(624))(); /***/ }), -/* 643 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66784,13 +66874,13 @@ var path = __webpack_require__(16); * Module dependencies */ -var Snapdragon = __webpack_require__(540); -utils.define = __webpack_require__(610); -utils.diff = __webpack_require__(627); -utils.extend = __webpack_require__(611); -utils.pick = __webpack_require__(628); -utils.typeOf = __webpack_require__(644); -utils.unique = __webpack_require__(513); +var Snapdragon = __webpack_require__(541); +utils.define = __webpack_require__(611); +utils.diff = __webpack_require__(628); +utils.extend = __webpack_require__(612); +utils.pick = __webpack_require__(629); +utils.typeOf = __webpack_require__(645); +utils.unique = __webpack_require__(514); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -67087,7 +67177,7 @@ utils.unixify = function(options) { /***/ }), -/* 644 */ +/* 645 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -67222,7 +67312,7 @@ function isBuffer(val) { /***/ }), -/* 645 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67241,9 +67331,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(646); -var reader_1 = __webpack_require__(659); -var fs_stream_1 = __webpack_require__(663); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_stream_1 = __webpack_require__(664); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -67304,15 +67394,15 @@ exports.default = ReaderAsync; /***/ }), -/* 646 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(647); -const readdirAsync = __webpack_require__(655); -const readdirStream = __webpack_require__(658); +const readdirSync = __webpack_require__(648); +const readdirAsync = __webpack_require__(656); +const readdirStream = __webpack_require__(659); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -67396,7 +67486,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 647 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67404,11 +67494,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(648); +const DirectoryReader = __webpack_require__(649); let syncFacade = { - fs: __webpack_require__(653), - forEach: __webpack_require__(654), + fs: __webpack_require__(654), + forEach: __webpack_require__(655), sync: true }; @@ -67437,7 +67527,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 648 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67446,9 +67536,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(28).Readable; const EventEmitter = __webpack_require__(46).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(649); -const stat = __webpack_require__(651); -const call = __webpack_require__(652); +const normalizeOptions = __webpack_require__(650); +const stat = __webpack_require__(652); +const call = __webpack_require__(653); /** * Asynchronously reads the contents of a directory and streams the results @@ -67824,14 +67914,14 @@ module.exports = DirectoryReader; /***/ }), -/* 649 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(650); +const globToRegExp = __webpack_require__(651); module.exports = normalizeOptions; @@ -68008,7 +68098,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 650 */ +/* 651 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -68145,13 +68235,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 651 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(652); +const call = __webpack_require__(653); module.exports = stat; @@ -68226,7 +68316,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 652 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68287,14 +68377,14 @@ function callOnce (fn) { /***/ }), -/* 653 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(652); +const call = __webpack_require__(653); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -68358,7 +68448,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 654 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68387,7 +68477,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 655 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68395,12 +68485,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(656); -const DirectoryReader = __webpack_require__(648); +const maybe = __webpack_require__(657); +const DirectoryReader = __webpack_require__(649); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(657), + forEach: __webpack_require__(658), async: true }; @@ -68442,7 +68532,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 656 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68469,7 +68559,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 657 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68505,7 +68595,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 658 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68513,11 +68603,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(648); +const DirectoryReader = __webpack_require__(649); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(657), + forEach: __webpack_require__(658), async: true }; @@ -68537,16 +68627,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 659 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(660); -var entry_1 = __webpack_require__(662); -var pathUtil = __webpack_require__(661); +var deep_1 = __webpack_require__(661); +var entry_1 = __webpack_require__(663); +var pathUtil = __webpack_require__(662); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -68612,14 +68702,14 @@ exports.default = Reader; /***/ }), -/* 660 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(661); -var patternUtils = __webpack_require__(494); +var pathUtils = __webpack_require__(662); +var patternUtils = __webpack_require__(495); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -68702,7 +68792,7 @@ exports.default = DeepFilter; /***/ }), -/* 661 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68733,14 +68823,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 662 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(661); -var patternUtils = __webpack_require__(494); +var pathUtils = __webpack_require__(662); +var patternUtils = __webpack_require__(495); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -68825,7 +68915,7 @@ exports.default = EntryFilter; /***/ }), -/* 663 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68845,8 +68935,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(28); -var fsStat = __webpack_require__(664); -var fs_1 = __webpack_require__(668); +var fsStat = __webpack_require__(665); +var fs_1 = __webpack_require__(669); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -68896,14 +68986,14 @@ exports.default = FileSystemStream; /***/ }), -/* 664 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(665); -const statProvider = __webpack_require__(667); +const optionsManager = __webpack_require__(666); +const statProvider = __webpack_require__(668); /** * Asynchronous API. */ @@ -68934,13 +69024,13 @@ exports.statSync = statSync; /***/ }), -/* 665 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(666); +const fsAdapter = __webpack_require__(667); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -68953,7 +69043,7 @@ exports.prepare = prepare; /***/ }), -/* 666 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68976,7 +69066,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 667 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69028,7 +69118,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 668 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69059,7 +69149,7 @@ exports.default = FileSystem; /***/ }), -/* 669 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69079,9 +69169,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(28); -var readdir = __webpack_require__(646); -var reader_1 = __webpack_require__(659); -var fs_stream_1 = __webpack_require__(663); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_stream_1 = __webpack_require__(664); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -69149,7 +69239,7 @@ exports.default = ReaderStream; /***/ }), -/* 670 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69168,9 +69258,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(646); -var reader_1 = __webpack_require__(659); -var fs_sync_1 = __webpack_require__(671); +var readdir = __webpack_require__(647); +var reader_1 = __webpack_require__(660); +var fs_sync_1 = __webpack_require__(672); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -69230,7 +69320,7 @@ exports.default = ReaderSync; /***/ }), -/* 671 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69249,8 +69339,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(664); -var fs_1 = __webpack_require__(668); +var fsStat = __webpack_require__(665); +var fs_1 = __webpack_require__(669); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -69296,7 +69386,7 @@ exports.default = FileSystemSync; /***/ }), -/* 672 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69312,7 +69402,7 @@ exports.flatten = flatten; /***/ }), -/* 673 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69333,13 +69423,13 @@ exports.merge = merge; /***/ }), -/* 674 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(675); +const pathType = __webpack_require__(676); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -69405,13 +69495,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 675 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(676); +const pify = __webpack_require__(677); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -69454,7 +69544,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 676 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69545,17 +69635,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 677 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); const path = __webpack_require__(16); -const fastGlob = __webpack_require__(490); -const gitIgnore = __webpack_require__(678); -const pify = __webpack_require__(679); -const slash = __webpack_require__(680); +const fastGlob = __webpack_require__(491); +const gitIgnore = __webpack_require__(679); +const pify = __webpack_require__(680); +const slash = __webpack_require__(681); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -69653,7 +69743,7 @@ module.exports.sync = options => { /***/ }), -/* 678 */ +/* 679 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -70122,7 +70212,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 679 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70197,7 +70287,7 @@ module.exports = (input, options) => { /***/ }), -/* 680 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70215,17 +70305,17 @@ module.exports = input => { /***/ }), -/* 681 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const {Buffer} = __webpack_require__(682); -const CpFileError = __webpack_require__(684); -const fs = __webpack_require__(686); -const ProgressEmitter = __webpack_require__(688); +const {Buffer} = __webpack_require__(683); +const CpFileError = __webpack_require__(685); +const fs = __webpack_require__(687); +const ProgressEmitter = __webpack_require__(689); const cpFile = (source, destination, options) => { if (!source || !destination) { @@ -70379,11 +70469,11 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 682 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { /* eslint-disable node/no-deprecated-api */ -var buffer = __webpack_require__(683) +var buffer = __webpack_require__(684) var Buffer = buffer.Buffer // alternative to using Object.keys for old browsers @@ -70447,18 +70537,18 @@ SafeBuffer.allocUnsafeSlow = function (size) { /***/ }), -/* 683 */ +/* 684 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 684 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(685); +const NestedError = __webpack_require__(686); class CpFileError extends NestedError { constructor(message, nested) { @@ -70472,7 +70562,7 @@ module.exports = CpFileError; /***/ }), -/* 685 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(44); @@ -70526,15 +70616,15 @@ module.exports = NestedError; /***/ }), -/* 686 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(22); const makeDir = __webpack_require__(115); -const pify = __webpack_require__(687); -const CpFileError = __webpack_require__(684); +const pify = __webpack_require__(688); +const CpFileError = __webpack_require__(685); const fsP = pify(fs); @@ -70679,7 +70769,7 @@ if (fs.copyFileSync) { /***/ }), -/* 687 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70754,7 +70844,7 @@ module.exports = (input, options) => { /***/ }), -/* 688 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70795,12 +70885,12 @@ module.exports = ProgressEmitter; /***/ }), -/* 689 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(690); +const NestedError = __webpack_require__(691); class CpyError extends NestedError { constructor(message, nested) { @@ -70814,7 +70904,7 @@ module.exports = CpyError; /***/ }), -/* 690 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -70870,7 +70960,7 @@ module.exports = NestedError; /***/ }), -/* 691 */ +/* 692 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index dafe73ec7d1d61..34a56615ed43a1 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -52,7 +52,7 @@ "ora": "^1.4.0", "prettier": "^1.18.2", "read-pkg": "^5.2.0", - "rxjs": "^6.2.1", + "rxjs": "^6.5.3", "spawn-sync": "^1.0.15", "string-replace-loader": "^2.2.0", "strip-ansi": "^4.0.0", diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 113783f66bc595..8c5358c82208df 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -25,7 +25,7 @@ "glob": "^7.1.2", "parse-link-header": "^1.0.1", "strip-ansi": "^5.2.0", - "rxjs": "^6.2.1", + "rxjs": "^6.5.3", "tar-fs": "^1.16.3", "tmp": "^0.1.0", "xml2js": "^0.4.22", diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index bc723de06f2159..3ddbe420ba2842 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -171,7 +171,7 @@ interface Props { navLinks$: Rx.Observable; recentlyAccessed$: Rx.Observable; forceAppSwitcherNavigation$: Rx.Observable; - helpExtension$: Rx.Observable; + helpExtension$: Rx.Observable; legacyMode: boolean; navControlsLeft$: Rx.Observable; navControlsRight$: Rx.Observable; diff --git a/src/core/public/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx index e2146f8d65bbae..688d76c9d75d9c 100644 --- a/src/core/public/chrome/ui/header/header_help_menu.tsx +++ b/src/core/public/chrome/ui/header/header_help_menu.tsx @@ -43,7 +43,7 @@ import { HeaderExtension } from './header_extension'; import { ChromeHelpExtension } from '../../chrome_service'; interface Props { - helpExtension$: Rx.Observable; + helpExtension$: Rx.Observable; intl: InjectedIntl; kibanaVersion: string; useDefaultContent?: boolean; diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index f61371c5437e6b..acae9d8ff0e704 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -577,45 +577,6 @@ test('exposes route details of incoming request to a route handler', async () => }); }); -describe('conditional compression', () => { - test('disables compression when there is a referer', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); - - const router = new Router('', logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (context, req, res) => - // we need the large body here so that compression would normally be used - res.ok({ body: 'hello'.repeat(500), headers: { 'Content-Type': 'text/html; charset=UTF-8' } }) - ); - registerRouter(router); - - await server.start(); - const response = await supertest(innerServer.listener) - .get('/') - .set('accept-encoding', 'gzip') - .set('referer', 'http://some-other-site/'); - - expect(response.header).not.toHaveProperty('content-encoding'); - }); - - test(`enables compression when there isn't a referer`, async () => { - const { registerRouter, server: innerServer } = await server.setup(config); - - const router = new Router('', logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (context, req, res) => - // we need the large body here so that compression will be used - res.ok({ body: 'hello'.repeat(500), headers: { 'Content-Type': 'text/html; charset=UTF-8' } }) - ); - registerRouter(router); - - await server.start(); - const response = await supertest(innerServer.listener) - .get('/') - .set('accept-encoding', 'gzip'); - - expect(response.header).toHaveProperty('content-encoding', 'gzip'); - }); -}); - describe('setup contract', () => { describe('#createSessionStorage', () => { it('creates session storage factory', async () => { diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index d6077200d3c757..3354324c12407d 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -96,7 +96,6 @@ export class HttpServer { const basePathService = new BasePath(config.basePath); this.setupBasePathRewrite(config, basePathService); - this.setupConditionalCompression(); return { registerRouter: this.registerRouter.bind(this), @@ -176,23 +175,6 @@ export class HttpServer { }); } - private setupConditionalCompression() { - if (this.server === undefined) { - throw new Error('Server is not created yet'); - } - - this.server.ext('onRequest', (request, h) => { - // whenever there is a referrer, don't use compression even if the client supports it - if (request.info.referrer !== '') { - this.log.debug( - `Not using compression because there is a referer: ${request.info.referrer}` - ); - request.info.acceptEncoding = ''; - } - return h.continue; - }); - } - private registerOnPostAuth(fn: OnPostAuthHandler) { if (this.server === undefined) { throw new Error('Server is not created yet'); diff --git a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts index f1f4da8d0b4d75..c0a6026708af3a 100644 --- a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts +++ b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts @@ -54,7 +54,7 @@ export async function findLegacyPluginSpecs(settings: unknown, loggerFactory: Lo invalidDirectoryError$: Observable<{ path: string }>; invalidPackError$: Observable<{ path: string }>; otherError$: Observable; - deprecation$: Observable; + deprecation$: Observable<{ spec: LegacyPluginSpec; message: string }>; invalidVersionSpec$: Observable; spec$: Observable; disabledSpec$: Observable; diff --git a/src/core/server/plugins/discovery/plugins_discovery.ts b/src/core/server/plugins/discovery/plugins_discovery.ts index 74e9dd709bb238..521d02e487df61 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.ts @@ -29,7 +29,7 @@ import { PluginsConfig } from '../plugins_config'; import { PluginDiscoveryError } from './plugin_discovery_error'; import { parseManifest } from './plugin_manifest_parser'; -const fsReadDir$ = bindNodeCallback(readdir); +const fsReadDir$ = bindNodeCallback(readdir); const fsStat$ = bindNodeCallback(stat); /** diff --git a/src/dev/build/lib/scan_delete.ts b/src/dev/build/lib/scan_delete.ts index f1b2a02c11a435..cb4e64ce1b5f98 100644 --- a/src/dev/build/lib/scan_delete.ts +++ b/src/dev/build/lib/scan_delete.ts @@ -28,7 +28,7 @@ import { count, map, mergeAll, mergeMap } from 'rxjs/operators'; import { assertAbsolute } from './fs'; const getStat$ = Rx.bindNodeCallback(Fs.stat); -const getReadDir$ = Rx.bindNodeCallback(Fs.readdir); +const getReadDir$ = Rx.bindNodeCallback(Fs.readdir); interface Options { directory: string; diff --git a/src/fixtures/logstash_fields.js b/src/fixtures/logstash_fields.js index 0ae6c62def0f7c..ab96b69851b713 100644 --- a/src/fixtures/logstash_fields.js +++ b/src/fixtures/logstash_fields.js @@ -19,7 +19,7 @@ import { castEsToKbnFieldTypeName } from '../plugins/data/common'; // eslint-disable-next-line max-len -import { shouldReadFieldFromDocValues } from '../legacy/server/index_patterns/service/lib/field_capabilities/should_read_field_from_doc_values'; +import { shouldReadFieldFromDocValues } from '../plugins/data/server'; function stubbedLogstashFields() { return [ diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/index.ts b/src/legacy/core_plugins/dashboard_embeddable_container/index.ts index 74682722e9b893..714203de203851 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/index.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/index.ts @@ -23,7 +23,6 @@ import { resolve } from 'path'; export default function(kibana: any) { return new kibana.Plugin({ uiExports: { - hacks: ['plugins/dashboard_embeddable_container/initialize'], styleSheetPaths: resolve(__dirname, 'public/index.scss'), }, }); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/index.scss b/src/legacy/core_plugins/dashboard_embeddable_container/public/index.scss index 88e570d1378938..548e85746f866d 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/index.scss +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/index.scss @@ -1,6 +1,3 @@ @import 'src/legacy/ui/public/styles/styling_constants'; -@import 'src/legacy/core_plugins/embeddable_api/public/variables'; -@import './np_ready/public/lib/embeddable/grid/index'; -@import './np_ready/public/lib/embeddable/panel/index'; -@import './np_ready/public/lib/embeddable/viewport/index'; +@import '../../../../plugins/dashboard_embeddable_container/public/index'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/initialize.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/initialize.ts index a4bc3cf17026c3..9880b336e76e5c 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/initialize.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/initialize.ts @@ -16,5 +16,3 @@ * specific language governing permissions and limitations * under the License. */ - -import './np_ready/public/legacy'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/index.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/index.ts index 1dad1b488590e5..d8c0de2bce3f4a 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/index.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/index.ts @@ -17,13 +17,4 @@ * under the License. */ -import { PluginInitializerContext } from '../../../../../../core/public'; -import { DashboardEmbeddableContainerPublicPlugin } from './plugin'; - -export * from './lib'; - -export function plugin(initializerContext: PluginInitializerContext) { - return new DashboardEmbeddableContainerPublicPlugin(initializerContext); -} - -export { DashboardEmbeddableContainerPublicPlugin as Plugin }; +export * from '../../../../../../plugins/dashboard_embeddable_container/public'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts index ccb04d8c2e0276..9880b336e76e5c 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts @@ -16,32 +16,3 @@ * specific language governing permissions and limitations * under the License. */ - -/* eslint-disable @kbn/eslint/no-restricted-paths */ -import { npSetup, npStart } from 'ui/new_platform'; -import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; -import { ExitFullScreenButton } from 'ui/exit_full_screen'; -/* eslint-enable @kbn/eslint/no-restricted-paths */ - -import { plugin } from '.'; -import { - setup as embeddableSetup, - start as embeddableStart, -} from '../../../../embeddable_api/public/np_ready/public/legacy'; - -const pluginInstance = plugin({} as any); - -export const setup = pluginInstance.setup(npSetup.core, { - embeddable: embeddableSetup, - uiActions: npSetup.plugins.uiActions, -}); - -export const start = pluginInstance.start(npStart.core, { - embeddable: embeddableStart, - inspector: npStart.plugins.inspector, - __LEGACY: { - SavedObjectFinder, - ExitFullScreenButton, - }, - uiActions: npStart.plugins.uiActions, -}); diff --git a/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts b/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts index 5db3d779d12fae..8d2337264d02fd 100644 --- a/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts +++ b/src/legacy/core_plugins/data/public/filter/action/apply_filter_action.ts @@ -18,21 +18,20 @@ */ import { i18n } from '@kbn/i18n'; -import { Filter } from '@kbn/es-query'; import { CoreStart } from 'src/core/public'; import { IAction, createAction, IncompatibleActionError, } from '../../../../../../plugins/ui_actions/public'; -import { FilterManager } from '../../../../../../plugins/data/public'; +import { FilterManager, esFilters } from '../../../../../../plugins/data/public'; import { TimefilterContract, changeTimeFilter, extractTimeFilter } from '../../timefilter'; import { applyFiltersPopover } from '../apply_filters/apply_filters_popover'; import { IndexPatternsStart } from '../../index_patterns'; export const GLOBAL_APPLY_FILTER_ACTION = 'GLOBAL_APPLY_FILTER_ACTION'; interface ActionContext { - filters: Filter[]; + filters: esFilters.Filter[]; timeFieldName?: string; } @@ -64,7 +63,7 @@ export function createFilterAction( throw new IncompatibleActionError(); } - let selectedFilters: Filter[] = filters; + let selectedFilters: esFilters.Filter[] = filters; if (selectedFilters.length > 1) { const indexPatterns = await Promise.all( @@ -73,7 +72,7 @@ export function createFilterAction( }) ); - const filterSelectionPromise: Promise = new Promise(resolve => { + const filterSelectionPromise: Promise = new Promise(resolve => { const overlay = overlays.openModal( applyFiltersPopover( filters, @@ -82,7 +81,7 @@ export function createFilterAction( overlay.close(); resolve([]); }, - (filterSelection: Filter[]) => { + (filterSelection: esFilters.Filter[]) => { overlay.close(); resolve(filterSelection); } diff --git a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx index 8fc6b33f3f68a3..e9d05d6340e584 100644 --- a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx +++ b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx @@ -28,19 +28,18 @@ import { EuiModalHeaderTitle, EuiSwitch, } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { IndexPattern } from '../../index_patterns'; import { getFilterDisplayText } from '../filter_bar/filter_editor/lib/get_filter_display_text'; -import { mapAndFlattenFilters } from '../../../../../../plugins/data/public'; +import { mapAndFlattenFilters, esFilters } from '../../../../../../plugins/data/public'; import { getDisplayValueFromFilter } from '../filter_bar/filter_editor/lib/get_display_value'; interface Props { - filters: Filter[]; + filters: esFilters.Filter[]; indexPatterns: IndexPattern[]; onCancel: () => void; - onSubmit: (filters: Filter[]) => void; + onSubmit: (filters: esFilters.Filter[]) => void; } interface State { @@ -58,7 +57,7 @@ export class ApplyFiltersPopoverContent extends Component { isFilterSelected: props.filters.map(() => true), }; } - private getLabel(filter: Filter) { + private getLabel(filter: esFilters.Filter) { const filterDisplayValue = getDisplayValueFromFilter(filter, this.props.indexPatterns); return getFilterDisplayText(filter, filterDisplayValue); } diff --git a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filters_popover.tsx b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filters_popover.tsx index 0687701429866d..41f757e726c40b 100644 --- a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filters_popover.tsx +++ b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filters_popover.tsx @@ -18,15 +18,15 @@ */ import { EuiModal, EuiOverlayMask } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; import React, { Component } from 'react'; import { ApplyFiltersPopoverContent } from './apply_filter_popover_content'; import { IndexPattern } from '../../index_patterns/index_patterns'; +import { esFilters } from '../../../../../../plugins/data/public'; interface Props { - filters: Filter[]; + filters: esFilters.Filter[]; onCancel: () => void; - onSubmit: (filters: Filter[]) => void; + onSubmit: (filters: esFilters.Filter[]) => void; indexPatterns: IndexPattern[]; } @@ -56,9 +56,9 @@ export class ApplyFiltersPopover extends Component { } type cancelFunction = () => void; -type submitFunction = (filters: Filter[]) => void; +type submitFunction = (filters: esFilters.Filter[]) => void; export const applyFiltersPopover = ( - filters: Filter[], + filters: esFilters.Filter[], indexPatterns: IndexPattern[], onCancel: cancelFunction, onSubmit: submitFunction diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_group.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_group.scss index 3c90e18aecd5d6..1c47c28097454e 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_group.scss +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_group.scss @@ -11,6 +11,10 @@ margin-top: $euiSizeXS; } +.globalFilterBar__addButton { + min-height: $euiSizeL + $euiSizeXS; // same height as the badges +} + // sass-lint:disable quotes .globalFilterGroup__branch { padding: $euiSizeS $euiSizeM 0 0; @@ -40,4 +44,3 @@ margin-top: $euiSize * -1; } } - diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_item.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_item.scss index caf3b0b796b9e2..84538a62ca005f 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_item.scss +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/_global_filter_item.scss @@ -1,5 +1,5 @@ -@import '@elastic/eui/src/components/form/mixins'; @import '@elastic/eui/src/components/form/variables'; +@import '@elastic/eui/src/components/form/mixins'; /** * 1. Allow wrapping of long filter items @@ -19,11 +19,17 @@ &:not(.globalFilterItem-isDisabled) { @include euiFormControlDefaultShadow; + box-shadow: #{$euiFormControlBoxShadow}, inset 0 0 0 1px $kbnGlobalFilterItemBorderColor; // Make the actual border more visible + } + + &:focus-within { + animation: none !important; // Remove focus ring animation otherwise it overrides simulated border via box-shadow } } .globalFilterItem-isDisabled { - background-color: transparentize($euiColorLightShade, .4); + color: $euiColorDarkShade; + background-color: transparentize($euiColorLightShade, 0.5); text-decoration: line-through; font-weight: $euiFontWeightRegular; font-style: italic; @@ -39,12 +45,22 @@ bottom: 0; left: 0; width: $euiSizeXS; - background-color: $euiColorVis0; + background-color: $kbnGlobalFilterItemBorderColor; border-top-left-radius: $euiBorderRadius / 2; border-bottom-left-radius: $euiBorderRadius / 2; } } +.globalFilterItem-isExcluded { + &:not(.globalFilterItem-isDisabled) { + box-shadow: #{$euiFormControlBoxShadow}, inset 0 0 0 1px $kbnGlobalFilterItemBorderColorExcluded; + + &::before { + background-color: $kbnGlobalFilterItemPinnedColorExcluded; + } + } +} + .globalFilterItem__editorForm { padding: $euiSizeM; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss index 3c57b7fe2ca3ad..5333aff8b87da3 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/_index.scss @@ -1,2 +1,3 @@ +@import 'variables'; @import 'global_filter_group'; @import 'global_filter_item'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/_variables.scss b/src/legacy/core_plugins/data/public/filter/filter_bar/_variables.scss new file mode 100644 index 00000000000000..3a9a0df4332c81 --- /dev/null +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/_variables.scss @@ -0,0 +1,3 @@ +$kbnGlobalFilterItemBorderColor: tintOrShade($euiColorMediumShade, 35%, 20%); +$kbnGlobalFilterItemBorderColorExcluded: tintOrShade($euiColorDanger, 70%, 50%); +$kbnGlobalFilterItemPinnedColorExcluded: tintOrShade($euiColorDanger, 30%, 20%); diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 066adb1e3275e8..333e1e328651d4 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -18,16 +18,6 @@ */ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui'; -import { - buildEmptyFilter, - disableFilter, - enableFilter, - Filter, - pinFilter, - toggleFilterDisabled, - toggleFilterNegated, - unpinFilter, -} from '@kbn/es-query'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; @@ -38,10 +28,11 @@ import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; import { useKibana, KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; +import { esFilters } from '../../../../../../plugins/data/public'; interface Props { - filters: Filter[]; - onFiltersUpdated?: (filters: Filter[]) => void; + filters: esFilters.Filter[]; + onFiltersUpdated?: (filters: esFilters.Filter[]) => void; className: string; indexPatterns: IndexPattern[]; intl: InjectedIntl; @@ -87,7 +78,7 @@ function FilterBarUI(props: Props) { return content; } - function onFiltersUpdated(filters: Filter[]) { + function onFiltersUpdated(filters: esFilters.Filter[]) { if (props.onFiltersUpdated) { props.onFiltersUpdated(filters); } @@ -112,13 +103,14 @@ function FilterBarUI(props: Props) { const isPinned = uiSettings!.get('filters:pinnedByDefault'); const [indexPattern] = props.indexPatterns; const index = indexPattern && indexPattern.id; - const newFilter = buildEmptyFilter(isPinned, index); + const newFilter = esFilters.buildEmptyFilter(isPinned, index); const button = ( setIsAddFilterPopoverOpen(true)} data-test-subj="addFilter" + className="globalFilterBar__addButton" > +{' '} void; + onSubmit: (filter: esFilters.Filter) => void; onCancel: () => void; intl: InjectedIntl; } @@ -379,7 +379,9 @@ class FilterEditorUI extends Component { private getFieldFromFilter() { const indexPattern = this.getIndexPatternFromFilter(); - return indexPattern && getFieldFromFilter(this.props.filter as FieldFilter, indexPattern); + return ( + indexPattern && getFieldFromFilter(this.props.filter as esFilters.FieldFilter, indexPattern) + ); } private getSelectedOperator() { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts index 734c5d00e58d5d..dbff5096f2287d 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts @@ -17,7 +17,6 @@ * under the License. */ -import { FilterStateStore, toggleFilterNegated } from '@kbn/es-query'; import { mockFields, mockIndexPattern } from '../../../../index_patterns'; import { IndexPattern, Field } from '../../../../index'; import { @@ -42,6 +41,7 @@ import { existsFilter } from './fixtures/exists_filter'; import { phraseFilter } from './fixtures/phrase_filter'; import { phrasesFilter } from './fixtures/phrases_filter'; import { rangeFilter } from './fixtures/range_filter'; +import { esFilters } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); @@ -81,7 +81,7 @@ describe('Filter editor utils', () => { }); it('should return "is not" for phrase filter', () => { - const negatedPhraseFilter = toggleFilterNegated(phraseFilter); + const negatedPhraseFilter = esFilters.toggleFilterNegated(phraseFilter); const operator = getOperatorFromFilter(negatedPhraseFilter); expect(operator).not.toBeUndefined(); expect(operator && operator.type).toBe('phrase'); @@ -96,7 +96,7 @@ describe('Filter editor utils', () => { }); it('should return "is not one of" for negated phrases filter', () => { - const negatedPhrasesFilter = toggleFilterNegated(phrasesFilter); + const negatedPhrasesFilter = esFilters.toggleFilterNegated(phrasesFilter); const operator = getOperatorFromFilter(negatedPhrasesFilter); expect(operator).not.toBeUndefined(); expect(operator && operator.type).toBe('phrases'); @@ -111,7 +111,7 @@ describe('Filter editor utils', () => { }); it('should return "is not between" for negated range filter', () => { - const negatedRangeFilter = toggleFilterNegated(rangeFilter); + const negatedRangeFilter = esFilters.toggleFilterNegated(rangeFilter); const operator = getOperatorFromFilter(negatedRangeFilter); expect(operator).not.toBeUndefined(); expect(operator && operator.type).toBe('range'); @@ -126,7 +126,7 @@ describe('Filter editor utils', () => { }); it('should return "does not exists" for negated exists filter', () => { - const negatedExistsFilter = toggleFilterNegated(existsFilter); + const negatedExistsFilter = esFilters.toggleFilterNegated(existsFilter); const operator = getOperatorFromFilter(negatedExistsFilter); expect(operator).not.toBeUndefined(); expect(operator && operator.type).toBe('exists'); @@ -246,7 +246,7 @@ describe('Filter editor utils', () => { it('should build phrase filters', () => { const params = 'foo'; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], @@ -268,7 +268,7 @@ describe('Filter editor utils', () => { it('should build phrases filters', () => { const params = ['foo', 'bar']; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], @@ -290,7 +290,7 @@ describe('Filter editor utils', () => { it('should build range filters', () => { const params = { from: 'foo', to: 'qux' }; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], @@ -311,7 +311,7 @@ describe('Filter editor utils', () => { it('should build exists filters', () => { const params = undefined; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], @@ -332,7 +332,7 @@ describe('Filter editor utils', () => { it('should include disabled state', () => { const params = undefined; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], @@ -348,7 +348,7 @@ describe('Filter editor utils', () => { it('should negate based on operator', () => { const params = undefined; const alias = 'bar'; - const state = FilterStateStore.APP_STATE; + const state = esFilters.FilterStateStore.APP_STATE; const filter = buildFilter( mockedIndexPattern, mockedFields[0], diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts index f0628f03c173e1..b7d20526a6b924 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts @@ -18,42 +18,30 @@ */ import dateMath from '@elastic/datemath'; -import { - buildExistsFilter, - buildPhraseFilter, - buildPhrasesFilter, - buildRangeFilter, - FieldFilter, - Filter, - FilterMeta, - FilterStateStore, - PhraseFilter, - PhrasesFilter, - RangeFilter, -} from '@kbn/es-query'; import { omit } from 'lodash'; import { Ipv4Address } from '../../../../../../../../plugins/kibana_utils/public'; import { Field, IndexPattern, isFilterable } from '../../../../index_patterns'; import { FILTER_OPERATORS, Operator } from './filter_operators'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export function getIndexPatternFromFilter( - filter: Filter, + filter: esFilters.Filter, indexPatterns: IndexPattern[] ): IndexPattern | undefined { return indexPatterns.find(indexPattern => indexPattern.id === filter.meta.index); } -export function getFieldFromFilter(filter: FieldFilter, indexPattern: IndexPattern) { +export function getFieldFromFilter(filter: esFilters.FieldFilter, indexPattern: IndexPattern) { return indexPattern.fields.find(field => field.name === filter.meta.key); } -export function getOperatorFromFilter(filter: Filter) { +export function getOperatorFromFilter(filter: esFilters.Filter) { return FILTER_OPERATORS.find(operator => { return filter.meta.type === operator.type && filter.meta.negate === operator.negate; }); } -export function getQueryDslFromFilter(filter: Filter) { +export function getQueryDslFromFilter(filter: esFilters.Filter) { return omit(filter, ['$state', 'meta']); } @@ -67,16 +55,16 @@ export function getOperatorOptions(field: Field) { }); } -export function getFilterParams(filter: Filter) { +export function getFilterParams(filter: esFilters.Filter) { switch (filter.meta.type) { case 'phrase': - return (filter as PhraseFilter).meta.params.query; + return (filter as esFilters.PhraseFilter).meta.params.query; case 'phrases': - return (filter as PhrasesFilter).meta.params; + return (filter as esFilters.PhrasesFilter).meta.params; case 'range': return { - from: (filter as RangeFilter).meta.params.gte, - to: (filter as RangeFilter).meta.params.lt, + from: (filter as esFilters.RangeFilter).meta.params.gte, + to: (filter as esFilters.RangeFilter).meta.params.lt, }; } } @@ -133,8 +121,8 @@ export function buildFilter( disabled: boolean, params: any, alias: string | null, - store: FilterStateStore -): Filter { + store: esFilters.FilterStateStore +): esFilters.Filter { const filter = buildBaseFilter(indexPattern, field, operator, params); filter.meta.alias = alias; filter.meta.negate = operator.negate; @@ -148,17 +136,17 @@ function buildBaseFilter( field: Field, operator: Operator, params: any -): Filter { +): esFilters.Filter { switch (operator.type) { case 'phrase': - return buildPhraseFilter(field, params, indexPattern); + return esFilters.buildPhraseFilter(field, params, indexPattern); case 'phrases': - return buildPhrasesFilter(field, params, indexPattern); + return esFilters.buildPhrasesFilter(field, params, indexPattern); case 'range': const newParams = { gte: params.from, lt: params.to }; - return buildRangeFilter(field, newParams, indexPattern); + return esFilters.buildRangeFilter(field, newParams, indexPattern); case 'exists': - return buildExistsFilter(field, indexPattern); + return esFilters.buildExistsFilter(field, indexPattern); default: throw new Error(`Unknown operator type: ${operator.type}`); } @@ -170,10 +158,10 @@ export function buildCustomFilter( disabled: boolean, negate: boolean, alias: string | null, - store: FilterStateStore -): Filter { - const meta: FilterMeta = { index, type: 'custom', disabled, negate, alias }; - const filter: Filter = { ...queryDsl, meta }; + store: esFilters.FilterStateStore +): esFilters.Filter { + const meta: esFilters.FilterMeta = { index, type: 'custom', disabled, negate, alias }; + const filter: esFilters.Filter = { ...queryDsl, meta }; filter.$state = { store }; return filter; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts index a17f767006f3ea..5af97818f9bfbd 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts @@ -17,9 +17,9 @@ * under the License. */ -import { ExistsFilter, FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../../plugins/data/public'; -export const existsFilter: ExistsFilter = { +export const existsFilter: esFilters.ExistsFilter = { meta: { index: 'logstash-*', negate: false, @@ -29,6 +29,6 @@ export const existsFilter: ExistsFilter = { alias: null, }, $state: { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }, }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts index 77bb8e06c801ad..b6c8b9905e6b33 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts @@ -17,9 +17,9 @@ * under the License. */ -import { FilterStateStore, PhraseFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../../plugins/data/public'; -export const phraseFilter: PhraseFilter = { +export const phraseFilter: esFilters.PhraseFilter = { meta: { negate: false, index: 'logstash-*', @@ -33,6 +33,6 @@ export const phraseFilter: PhraseFilter = { }, }, $state: { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }, }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts index e86c3ee1318e34..2e2ba4f798bddf 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts @@ -17,9 +17,9 @@ * under the License. */ -import { FilterStateStore, PhrasesFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../../plugins/data/public'; -export const phrasesFilter: PhrasesFilter = { +export const phrasesFilter: esFilters.PhrasesFilter = { meta: { index: 'logstash-*', type: 'phrases', @@ -31,6 +31,6 @@ export const phrasesFilter: PhrasesFilter = { alias: null, }, $state: { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }, }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts index 46a5181450feae..c6438e30ecec61 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts @@ -17,9 +17,9 @@ * under the License. */ -import { FilterStateStore, RangeFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../../plugins/data/public'; -export const rangeFilter: RangeFilter = { +export const rangeFilter: esFilters.RangeFilter = { meta: { index: 'logstash-*', negate: false, @@ -34,7 +34,7 @@ export const rangeFilter: RangeFilter = { }, }, $state: { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }, range: {}, }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts index 551b99d01b7da8..d8af7b3e97ad23 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts @@ -18,7 +18,7 @@ */ import { get } from 'lodash'; -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../plugins/data/public'; import { IndexPattern } from '../../../../index_patterns/index_patterns'; import { Field } from '../../../../index_patterns/fields'; import { getIndexPatternFromFilter } from './filter_editor_utils'; @@ -33,7 +33,10 @@ function getValueFormatter(indexPattern?: IndexPattern, key?: string) { return format; } -export function getDisplayValueFromFilter(filter: Filter, indexPatterns: IndexPattern[]): string { +export function getDisplayValueFromFilter( + filter: esFilters.Filter, + indexPatterns: IndexPattern[] +): string { const indexPattern = getIndexPatternFromFilter(filter, indexPatterns); if (typeof filter.meta.value === 'function') { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.ts deleted file mode 100644 index 73ee1a69a2ce38..00000000000000 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.ts +++ /dev/null @@ -1,53 +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. - */ - -import { Filter } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; -import { existsOperator, isOneOfOperator } from './filter_operators'; - -export function getFilterDisplayText(filter: Filter, filterDisplayName: string) { - const prefix = filter.meta.negate - ? ` ${i18n.translate('data.filter.filterBar.negatedFilterPrefix', { - defaultMessage: 'NOT ', - })}` - : ''; - - if (filter.meta.alias !== null) { - return `${prefix}${filter.meta.alias}`; - } - - switch (filter.meta.type) { - case 'exists': - return `${prefix}${filter.meta.key} ${existsOperator.message}`; - case 'geo_bounding_box': - return `${prefix}${filter.meta.key}: ${filterDisplayName}`; - case 'geo_polygon': - return `${prefix}${filter.meta.key}: ${filterDisplayName}`; - case 'phrase': - return `${prefix}${filter.meta.key}: ${filterDisplayName}`; - case 'phrases': - return `${prefix}${filter.meta.key} ${isOneOfOperator.message} ${filterDisplayName}`; - case 'query_string': - return `${prefix}${filterDisplayName}`; - case 'range': - return `${prefix}${filter.meta.key}: ${filterDisplayName}`; - default: - return `${prefix}${JSON.stringify(filter.query)}`; - } -} diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.tsx new file mode 100644 index 00000000000000..21abcd8510046d --- /dev/null +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_filter_display_text.tsx @@ -0,0 +1,102 @@ +/* + * 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, { Fragment } from 'react'; +import { EuiTextColor } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { existsOperator, isOneOfOperator } from './filter_operators'; +import { esFilters } from '../../../../../../../../plugins/data/public'; + +export function getFilterDisplayText(filter: esFilters.Filter, filterDisplayName: string) { + const prefixText = filter.meta.negate + ? ` ${i18n.translate('data.filter.filterBar.negatedFilterPrefix', { + defaultMessage: 'NOT ', + })}` + : ''; + const prefix = + filter.meta.negate && !filter.meta.disabled ? ( + {prefixText} + ) : ( + prefixText + ); + + if (filter.meta.alias !== null) { + return `${prefix}${filter.meta.alias}`; + } + + switch (filter.meta.type) { + case 'exists': + return ( + + {prefix} + {filter.meta.key} {existsOperator.message} + + ); + case 'geo_bounding_box': + return ( + + {prefix} + {filter.meta.key}: {filterDisplayName} + + ); + case 'geo_polygon': + return ( + + {prefix} + {filter.meta.key}: {filterDisplayName} + + ); + case 'phrase': + return ( + + {prefix} + {filter.meta.key}: {filterDisplayName} + + ); + case 'phrases': + return ( + + {prefix} + {filter.meta.key} {isOneOfOperator.message} {filterDisplayName} + + ); + case 'query_string': + return ( + + {prefix} + {filterDisplayName} + + ); + case 'range': + case 'phrase': + return ( + + {prefix} + {filter.meta.key}: {filterDisplayName} + + ); + default: + return ( + + {prefix} + {JSON.stringify(filter.query)} + + ); + } +} diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx index 2e98cbd306e9c6..50c1672333801e 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx @@ -18,13 +18,6 @@ */ import { EuiContextMenu, EuiPopover } from '@elastic/eui'; -import { - Filter, - isFilterPinned, - toggleFilterDisabled, - toggleFilterNegated, - toggleFilterPinned, -} from '@kbn/es-query'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; @@ -33,13 +26,14 @@ import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; import { getDisplayValueFromFilter } from './filter_editor/lib/get_display_value'; +import { esFilters } from '../../../../../../plugins/data/public'; interface Props { id: string; - filter: Filter; + filter: esFilters.Filter; indexPatterns: IndexPattern[]; className?: string; - onUpdate: (filter: Filter) => void; + onUpdate: (filter: esFilters.Filter) => void; onRemove: () => void; intl: InjectedIntl; uiSettings: UiSettingsClientContract; @@ -62,7 +56,7 @@ class FilterItemUI extends Component { 'globalFilterItem', { 'globalFilterItem-isDisabled': disabled, - 'globalFilterItem-isPinned': isFilterPinned(filter), + 'globalFilterItem-isPinned': esFilters.isFilterPinned(filter), 'globalFilterItem-isExcluded': negate, }, this.props.className @@ -91,7 +85,7 @@ class FilterItemUI extends Component { id: 0, items: [ { - name: isFilterPinned(filter) + name: esFilters.isFilterPinned(filter) ? this.props.intl.formatMessage({ id: 'data.filter.filterBar.unpinFilterButtonLabel', defaultMessage: 'Unpin', @@ -209,23 +203,23 @@ class FilterItemUI extends Component { }); }; - private onSubmit = (filter: Filter) => { + private onSubmit = (filter: esFilters.Filter) => { this.closePopover(); this.props.onUpdate(filter); }; private onTogglePinned = () => { - const filter = toggleFilterPinned(this.props.filter); + const filter = esFilters.toggleFilterPinned(this.props.filter); this.props.onUpdate(filter); }; private onToggleNegated = () => { - const filter = toggleFilterNegated(this.props.filter); + const filter = esFilters.toggleFilterNegated(this.props.filter); this.props.onUpdate(filter); }; private onToggleDisabled = () => { - const filter = toggleFilterDisabled(this.props.filter); + const filter = esFilters.toggleFilterDisabled(this.props.filter); this.props.onUpdate(filter); }; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_view/index.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_view/index.tsx index a7ea23efce49ed..6421691c4ef416 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_view/index.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_view/index.tsx @@ -17,14 +17,14 @@ * under the License. */ -import { EuiBadge } from '@elastic/eui'; -import { Filter, isFilterPinned } from '@kbn/es-query'; +import { EuiBadge, useInnerText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { SFC } from 'react'; import { getFilterDisplayText } from '../filter_editor/lib/get_filter_display_text'; +import { esFilters } from '../../../../../../../plugins/data/public'; interface Props { - filter: Filter; + filter: esFilters.Filter; displayName: string; [propName: string]: any; } @@ -36,12 +36,15 @@ export const FilterView: SFC = ({ displayName, ...rest }: Props) => { + const [ref, innerText] = useInnerText(); + const displayText = {getFilterDisplayText(filter, displayName)}; + let title = i18n.translate('data.filter.filterBar.moreFilterActionsMessage', { - defaultMessage: 'Filter: {displayText}. Select for more filter actions.', - values: { displayText: getFilterDisplayText(filter, displayName) }, + defaultMessage: 'Filter: {innerText}. Select for more filter actions.', + values: { innerText }, }); - if (isFilterPinned(filter)) { + if (esFilters.isFilterPinned(filter)) { title = `${i18n.translate('data.filter.filterBar.pinnedFilterPrefix', { defaultMessage: 'Pinned', })} ${title}`; @@ -72,7 +75,7 @@ export const FilterView: SFC = ({ })} {...rest} > - {getFilterDisplayText(filter, displayName)} + {displayText} ); }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts index aae9c0754a8d8d..08d5955d3fae9c 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts @@ -19,12 +19,11 @@ import sinon from 'sinon'; -import { FilterStateStore } from '@kbn/es-query'; import { FilterStateManager } from './filter_state_manager'; import { StubState } from './test_helpers/stub_state'; import { getFilter } from './test_helpers/get_stub_filter'; -import { FilterManager } from '../../../../../../plugins/data/public'; +import { FilterManager, esFilters } from '../../../../../../plugins/data/public'; import { coreMock } from '../../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); @@ -59,7 +58,7 @@ describe('filter_state_manager', () => { }); test('should NOT watch state until both app and global state are defined', done => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); globalStateStub.filters.push(f1); setTimeout(() => { @@ -72,8 +71,8 @@ describe('filter_state_manager', () => { appStateStub.save = sinon.stub(); globalStateStub.save = sinon.stub(); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.setFilters([f1, f2]); @@ -109,7 +108,7 @@ describe('filter_state_manager', () => { done(); }); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, true, true, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, true, true, 'age', 34); globalStateStub.filters.push(f1); }); @@ -122,7 +121,7 @@ describe('filter_state_manager', () => { done(); }); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); appStateStub.filters.push(f1); }); @@ -130,8 +129,8 @@ describe('filter_state_manager', () => { appStateStub.save = sinon.stub(); globalStateStub.save = sinon.stub(); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.setFilters([f1, f2]); @@ -143,8 +142,8 @@ describe('filter_state_manager', () => { appStateStub.save = sinon.stub(); globalStateStub.save = sinon.stub(); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.addFilters([f1, f2]); @@ -160,7 +159,7 @@ describe('filter_state_manager', () => { ** And triggers *another* filter manager update. */ test('should NOT re-trigger filter manager', done => { - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.setFilters([f1]); const setFiltersSpy = sinon.spy(filterManager, 'setFilters'); diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts index af8722c37c703c..61821b7ad45e94 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts @@ -17,11 +17,9 @@ * under the License. */ -import { FilterStateStore } from '@kbn/es-query'; - import _ from 'lodash'; import { State } from 'ui/state_management/state'; -import { FilterManager } from '../../../../../../plugins/data/public'; +import { FilterManager, esFilters } from '../../../../../../plugins/data/public'; type GetAppStateFunc = () => State | undefined | null; @@ -73,8 +71,8 @@ export class FilterStateManager { const newGlobalFilters = _.cloneDeep(globalFilters); const newAppFilters = _.cloneDeep(appFilters); - FilterManager.setFiltersStore(newAppFilters, FilterStateStore.APP_STATE); - FilterManager.setFiltersStore(newGlobalFilters, FilterStateStore.GLOBAL_STATE); + FilterManager.setFiltersStore(newAppFilters, esFilters.FilterStateStore.APP_STATE); + FilterManager.setFiltersStore(newGlobalFilters, esFilters.FilterStateStore.GLOBAL_STATE); this.filterManager.setFilters(newGlobalFilters.concat(newAppFilters)); }, 10); diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_stub_filter.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_stub_filter.ts index 20d9e236f49be8..5238efe5efa59c 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_stub_filter.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/get_stub_filter.ts @@ -17,15 +17,15 @@ * under the License. */ -import { Filter, FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../plugins/data/public'; export function getFilter( - store: FilterStateStore, + store: esFilters.FilterStateStore, disabled: boolean, negated: boolean, queryKey: string, queryValue: any -): Filter { +): esFilters.Filter { return { $state: { store, diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_state.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_state.ts index ab92016d1b9ab3..f0a4bdef0229d0 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_state.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/test_helpers/stub_state.ts @@ -19,11 +19,11 @@ import sinon from 'sinon'; -import { Filter } from '@kbn/es-query'; import { State } from 'ui/state_management/state'; +import { esFilters } from '../../../../../../../plugins/data/public'; export class StubState implements State { - filters: Filter[]; + filters: esFilters.Filter[]; save: sinon.SinonSpy; constructor() { diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts index d445e2e58cd542..4767b6d3a3ca7a 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts @@ -29,7 +29,7 @@ import { fieldFormats } from 'ui/registry/field_formats'; import { createIndexPatternCache } from './_pattern_cache'; import { IndexPattern } from './index_pattern'; -import { IndexPatternsApiClient } from './index_patterns_api_client'; +import { IndexPatternsApiClient, GetFieldsOptions } from './index_patterns_api_client'; const indexPatternCache = createIndexPatternCache(); @@ -93,6 +93,14 @@ export class IndexPatterns { }); }; + getFieldsForTimePattern = (options: GetFieldsOptions = {}) => { + return this.apiClient.getFieldsForTimePattern(options); + }; + + getFieldsForWildcard = (options: GetFieldsOptions = {}) => { + return this.apiClient.getFieldsForWildcard(options); + }; + clearCache = (id?: string) => { this.savedObjectsCache = null; if (id) { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index bfa29bef634623..9f03f7fd307789 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -33,7 +33,6 @@ import { import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react'; import { debounce, compact, isEqual } from 'lodash'; -import { documentationLinks } from 'ui/documentation_links'; import { Toast } from 'src/core/public'; import { AutocompleteSuggestion, @@ -348,7 +347,7 @@ export class QueryBarInputUI extends Component { suggestion.field.subType.nested && !this.services.storage.get('kibana.KQLNestedQuerySyntaxInfoOptOut') ) { - const notifications = this.services.notifications; + const { notifications, docLinks } = this.services; const onKQLNestedQuerySyntaxInfoOptOut = (toast: Toast) => { if (!this.services.storage) return; @@ -356,7 +355,7 @@ export class QueryBarInputUI extends Component { notifications!.toasts.remove(toast); }; - if (notifications) { + if (notifications && docLinks) { const toast = notifications.toasts.add({ title: this.props.intl.formatMessage({ id: 'data.query.queryBar.KQLNestedQuerySyntaxInfoTitle', @@ -372,7 +371,7 @@ export class QueryBarInputUI extends Component { Learn more in our {link}." values={{ link: ( - + { - return (filters: Filter[]) => { + return (filters: esFilters.Filter[]) => { data.query.filterManager.setFilters(filters); }; }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index 56116e155eb2f4..b73b8edb39e547 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -29,6 +29,7 @@ import { EuiPagination, EuiText, EuiSpacer, + EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -116,8 +117,6 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ const savedQueryPopoverButton = ( { setIsOpen(!isOpen); }} @@ -129,7 +128,8 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ })} data-test-subj="saved-query-management-popover-button" > - # + + ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index c7f8b02caf853f..d3a26239e1006c 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -18,7 +18,6 @@ */ import { compact } from 'lodash'; -import { Filter } from '@kbn/es-query'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; @@ -39,14 +38,15 @@ import { KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; import { IDataPluginServices } from '../../../types'; +import { esFilters } from '../../../../../../../plugins/data/public'; interface SearchBarInjectedDeps { kibana: KibanaReactContextValue; intl: InjectedIntl; timeHistory: TimeHistoryContract; // Filter bar - onFiltersUpdated?: (filters: Filter[]) => void; - filters?: Filter[]; + onFiltersUpdated?: (filters: esFilters.Filter[]) => void; + filters?: esFilters.Filter[]; // Date picker dateRangeFrom?: string; dateRangeTo?: string; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 0c677bea985365..ebde9d60b0b51e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -17,9 +17,9 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { RefreshInterval, TimeRange } from 'src/plugins/data/public'; import { Query } from '../../query/query_bar'; +import { esFilters } from '../../../../../../plugins/data/public'; export * from './components'; @@ -36,6 +36,6 @@ export interface SavedQueryAttributes { title: string; description: string; query: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; timefilter?: SavedQueryTimeFilter; } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index ac5fdb7fe99d54..415da8a2c32cc1 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -19,7 +19,7 @@ import { SavedQueryAttributes } from '../index'; import { createSavedQueryService } from './saved_query_service'; -import { FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../plugins/data/public'; const savedQueryAttributes: SavedQueryAttributes = { title: 'foo', @@ -43,7 +43,7 @@ const savedQueryAttributesWithFilters: SavedQueryAttributes = { filters: [ { query: { match_all: {} }, - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, negate: false, diff --git a/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts index 5e16120f3b3c25..df3e33060b01f2 100644 --- a/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.test.ts @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { RangeFilter } from '@kbn/es-query'; import { changeTimeFilter } from './change_time_filter'; import { TimeRange } from 'src/plugins/data/public'; import { timefilterServiceMock } from '../timefilter_service.mock'; +import { esFilters } from '../../../../../../plugins/data/public'; const timefilterMock = timefilterServiceMock.createSetupContract(); const timefilter = timefilterMock.timefilter; @@ -42,7 +42,7 @@ describe('changeTimeFilter()', () => { test('should change the timefilter to match the range gt/lt', () => { const filter: any = { range: { '@timestamp': { gt, lt } } }; - changeTimeFilter(timefilter, filter as RangeFilter); + changeTimeFilter(timefilter, filter as esFilters.RangeFilter); const { to, from } = timefilter.getTime(); @@ -52,7 +52,7 @@ describe('changeTimeFilter()', () => { test('should change the timefilter to match the range gte/lte', () => { const filter: any = { range: { '@timestamp': { gte: gt, lte: lt } } }; - changeTimeFilter(timefilter, filter as RangeFilter); + changeTimeFilter(timefilter, filter as esFilters.RangeFilter); const { to, from } = timefilter.getTime(); diff --git a/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts index 4780ddb6b4b448..7943aab3c151f6 100644 --- a/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/change_time_filter.ts @@ -19,10 +19,10 @@ import moment from 'moment'; import { keys } from 'lodash'; -import { RangeFilter } from '@kbn/es-query'; import { TimefilterContract } from '../timefilter'; +import { esFilters } from '../../../../../../plugins/data/public'; -export function convertRangeFilterToTimeRange(filter: RangeFilter) { +export function convertRangeFilterToTimeRange(filter: esFilters.RangeFilter) { const key = keys(filter.range)[0]; const values = filter.range[key]; @@ -32,6 +32,6 @@ export function convertRangeFilterToTimeRange(filter: RangeFilter) { }; } -export function changeTimeFilter(timeFilter: TimefilterContract, filter: RangeFilter) { +export function changeTimeFilter(timeFilter: TimefilterContract, filter: esFilters.RangeFilter) { timeFilter.setTime(convertRangeFilterToTimeRange(filter)); } diff --git a/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts index d55c9babeed796..981c50844c4f3e 100644 --- a/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.test.ts @@ -17,15 +17,23 @@ * under the License. */ -import { Filter, buildRangeFilter, buildQueryFilter, buildPhraseFilter } from '@kbn/es-query'; import { extractTimeFilter } from './extract_time_filter'; +import { esFilters } from '../../../../../../plugins/data/public'; describe('filter manager utilities', () => { describe('extractTimeFilter()', () => { test('should detect timeFilter', async () => { - const filters: Filter[] = [ - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'), - buildRangeFilter({ name: 'time' }, { gt: 1388559600000, lt: 1388646000000 }, 'logstash-*'), + const filters: esFilters.Filter[] = [ + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'logstash-*', + '' + ), + esFilters.buildRangeFilter( + { name: 'time' }, + { gt: 1388559600000, lt: 1388646000000 }, + 'logstash-*' + ), ]; const result = await extractTimeFilter('time', filters); @@ -34,9 +42,13 @@ describe('filter manager utilities', () => { }); test("should not return timeFilter when name doesn't match", async () => { - const filters: Filter[] = [ - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'), - buildRangeFilter({ name: '@timestamp' }, { from: 1, to: 2 }, 'logstash-*'), + const filters: esFilters.Filter[] = [ + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'logstash-*', + '' + ), + esFilters.buildRangeFilter({ name: '@timestamp' }, { from: 1, to: 2 }, 'logstash-*', ''), ]; const result = await extractTimeFilter('time', filters); @@ -45,9 +57,13 @@ describe('filter manager utilities', () => { }); test('should not return a non range filter, even when names match', async () => { - const filters: Filter[] = [ - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'logstash-*'), - buildPhraseFilter({ name: 'time' }, 'banana', 'logstash-*'), + const filters: esFilters.Filter[] = [ + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'logstash-*', + '' + ), + esFilters.buildPhraseFilter({ name: 'time' }, 'banana', 'logstash-*'), ]; const result = await extractTimeFilter('time', filters); diff --git a/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts index 22bda5b21295ee..4281610cb63e4e 100644 --- a/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts +++ b/src/legacy/core_plugins/data/public/timefilter/lib/extract_time_filter.ts @@ -18,13 +18,13 @@ */ import { keys, partition } from 'lodash'; -import { Filter, isRangeFilter, RangeFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; -export function extractTimeFilter(timeFieldName: string, filters: Filter[]) { - const [timeRangeFilter, restOfFilters] = partition(filters, (obj: Filter) => { +export function extractTimeFilter(timeFieldName: string, filters: esFilters.Filter[]) { + const [timeRangeFilter, restOfFilters] = partition(filters, (obj: esFilters.Filter) => { let key; - if (isRangeFilter(obj)) { + if (esFilters.isRangeFilter(obj)) { key = keys(obj.range)[0]; } @@ -33,6 +33,6 @@ export function extractTimeFilter(timeFieldName: string, filters: Filter[]) { return { restOfFilters, - timeRangeFilter: timeRangeFilter[0] as RangeFilter | undefined, + timeRangeFilter: timeRangeFilter[0] as esFilters.RangeFilter | undefined, }; } diff --git a/src/legacy/core_plugins/embeddable_api/public/index.scss b/src/legacy/core_plugins/embeddable_api/public/index.scss index b4e170e6cb65c7..3f1977b909c31c 100644 --- a/src/legacy/core_plugins/embeddable_api/public/index.scss +++ b/src/legacy/core_plugins/embeddable_api/public/index.scss @@ -1,5 +1,3 @@ @import 'src/legacy/ui/public/styles/styling_constants'; -@import './variables'; -@import '../../../../plugins/embeddable/public/lib/panel/index'; -@import '../../../../plugins/embeddable/public/lib/panel/panel_header/index'; +@import '../../../../plugins/embeddable/public/index'; diff --git a/src/legacy/core_plugins/expressions/public/np_ready/public/expression_renderer.tsx b/src/legacy/core_plugins/expressions/public/np_ready/public/expression_renderer.tsx index c7635017caef2e..208bdbe5d0288f 100644 --- a/src/legacy/core_plugins/expressions/public/np_ready/public/expression_renderer.tsx +++ b/src/legacy/core_plugins/expressions/public/np_ready/public/expression_renderer.tsx @@ -21,7 +21,7 @@ import { useRef, useEffect, useState } from 'react'; import React from 'react'; import classNames from 'classnames'; import { EuiLoadingChart, EuiProgress } from '@elastic/eui'; -import { ExpressionAST, IExpressionLoaderParams, IInterpreterErrorResult } from './types'; +import { ExpressionAST, IExpressionLoaderParams } from './types'; import { ExpressionLoader } from './loader'; // Accept all options of the runner as props except for the @@ -35,7 +35,7 @@ export interface ExpressionRendererProps extends IExpressionLoaderParams { interface State { isEmpty: boolean; isLoading: boolean; - error: null | Error; + error: null | { message: string }; } export type ExpressionRenderer = React.FC; @@ -82,7 +82,7 @@ export const ExpressionRendererImplementation = ({ } setState(prevState => ({ ...prevState, isLoading: true })); }); - handlerRef.current.render$.subscribe((item: number | IInterpreterErrorResult) => { + handlerRef.current.render$.subscribe(item => { if (!handlerRef.current) { return; } diff --git a/src/legacy/core_plugins/expressions/public/np_ready/public/render.ts b/src/legacy/core_plugins/expressions/public/np_ready/public/render.ts index 09a310d381c9e9..8475325a2c6258 100644 --- a/src/legacy/core_plugins/expressions/public/np_ready/public/render.ts +++ b/src/legacy/core_plugins/expressions/public/np_ready/public/render.ts @@ -23,17 +23,22 @@ import { share } from 'rxjs/operators'; import { event, RenderId, Data, IInterpreterRenderHandlers } from './types'; import { getRenderersRegistry } from './services'; +interface RenderError { + type: 'error'; + error: { type?: string; message: string }; +} + export type IExpressionRendererExtraHandlers = Record; export class ExpressionRenderHandler { - render$: Observable; + render$: Observable; update$: Observable; events$: Observable; private element: HTMLElement; private destroyFn?: any; private renderCount: number = 0; - private renderSubject: Rx.Subject; + private renderSubject: Rx.Subject; private eventsSubject: Rx.Subject; private updateSubject: Rx.Subject; private handlers: IInterpreterRenderHandlers; diff --git a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts index a8d89b44566932..9d7b4fb6d04806 100644 --- a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts +++ b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts @@ -17,10 +17,9 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { TimeRange } from '../../../../../../plugins/data/public'; import { Adapters } from '../../../../../../plugins/inspector/public'; import { Query } from '../../../../../../plugins/data/public'; -export { TimeRange, Adapters, Filter, Query }; +export { TimeRange, Adapters, Query }; export * from '../../../../../../plugins/expressions/public'; diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js index 9f0ed1dfb5097b..65b1d41fa82393 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js @@ -20,12 +20,8 @@ import _ from 'lodash'; import { FilterManager } from './filter_manager.js'; import { - buildPhraseFilter, - buildPhrasesFilter, - getPhraseFilterField, - getPhraseFilterValue, - isPhraseFilter, -} from '@kbn/es-query'; + esFilters, +} from '../../../../../../plugins/data/public'; export class PhraseFilterManager extends FilterManager { constructor(controlId, fieldName, indexPattern, queryFilter) { @@ -43,12 +39,12 @@ export class PhraseFilterManager extends FilterManager { createFilter(phrases) { let newFilter; if (phrases.length === 1) { - newFilter = buildPhraseFilter( + newFilter = esFilters.buildPhraseFilter( this.indexPattern.fields.getByName(this.fieldName), phrases[0], this.indexPattern); } else { - newFilter = buildPhrasesFilter( + newFilter = esFilters.buildPhrasesFilter( this.indexPattern.fields.getByName(this.fieldName), phrases, this.indexPattern); @@ -107,12 +103,12 @@ export class PhraseFilterManager extends FilterManager { } // single phrase filter - if (isPhraseFilter(kbnFilter)) { - if (getPhraseFilterField(kbnFilter) !== this.fieldName) { + if (esFilters.isPhraseFilter(kbnFilter)) { + if (esFilters.getPhraseFilterField(kbnFilter) !== this.fieldName) { return; } - return getPhraseFilterValue(kbnFilter); + return esFilters.getPhraseFilterValue(kbnFilter); } // single phrase filter from bool filter diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js index 1c8f5e2aa5a3e3..3a232fd8b543d7 100644 --- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js +++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js @@ -19,7 +19,7 @@ import _ from 'lodash'; import { FilterManager } from './filter_manager.js'; -import { buildRangeFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; // Convert slider value into ES range filter function toRange(sliderValue) { @@ -55,7 +55,7 @@ export class RangeFilterManager extends FilterManager { * @return {object} range filter */ createFilter(value) { - const newFilter = buildRangeFilter( + const newFilter = esFilters.buildRangeFilter( this.indexPattern.fields.getByName(this.fieldName), toRange(value), this.indexPattern); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx index 282c2e6a356bff..b614e00ba8cd08 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx @@ -34,6 +34,12 @@ interface NumberInputOptionProps { setValue: (paramName: ParamName, value: number | '') => void; } +/** + * Do not use this component anymore. + * Please, use NumberInputOption in 'required_number_input.tsx'. + * It is required for compatibility with TS 3.7.0 + * This should be removed in the future + */ function NumberInputOption({ disabled, error, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/required_number_input.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/required_number_input.tsx new file mode 100644 index 00000000000000..7b62016c4e502c --- /dev/null +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/required_number_input.tsx @@ -0,0 +1,87 @@ +/* + * 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, { ReactNode, useCallback, ChangeEvent } from 'react'; +import { EuiFormRow, EuiFieldNumber } from '@elastic/eui'; +import { useValidation } from './utils'; + +interface NumberInputOptionProps { + disabled?: boolean; + error?: ReactNode; + isInvalid?: boolean; + label?: React.ReactNode; + max?: number; + min?: number; + paramName: ParamName; + step?: number; + value: number | null; + 'data-test-subj'?: string; + setValue(paramName: ParamName, value: number | null): void; + setValidity(paramName: ParamName, isValid: boolean): void; +} + +/** + * Use only this component instead of NumberInputOption in 'number_input.tsx'. + * It is required for compatibility with TS 3.7.0 + * + * @param {number} props.value Should be numeric only + */ +function NumberInputOption({ + disabled, + error, + isInvalid, + label, + max, + min, + paramName, + step, + value, + setValue, + setValidity, + 'data-test-subj': dataTestSubj, +}: NumberInputOptionProps) { + const isValid = value !== null; + useValidation(setValidity, paramName, isValid); + + const onChange = useCallback( + (ev: ChangeEvent) => + setValue(paramName, isNaN(ev.target.valueAsNumber) ? null : ev.target.valueAsNumber), + [setValue, paramName] + ); + + return ( + + + + ); +} + +export { NumberInputOption }; diff --git a/packages/kbn-es-query/src/filters/lib/phrases_filter.ts b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/utils.ts similarity index 68% rename from packages/kbn-es-query/src/filters/lib/phrases_filter.ts rename to src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/utils.ts index 213afb409a0a69..d51631106dda76 100644 --- a/packages/kbn-es-query/src/filters/lib/phrases_filter.ts +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/utils.ts @@ -17,16 +17,18 @@ * under the License. */ -import { Filter, FilterMeta } from './meta_filter'; +import { useEffect } from 'react'; -export type PhrasesFilterMeta = FilterMeta & { - params: string[]; // The unformatted values - field?: string; -}; +function useValidation( + setValidity: (paramName: ParamName, isValid: boolean) => void, + paramName: ParamName, + isValid: boolean +) { + useEffect(() => { + setValidity(paramName, isValid); -export type PhrasesFilter = Filter & { - meta: PhrasesFilterMeta; -}; + return () => setValidity(paramName, true); + }, [isValid, paramName, setValidity]); +} -export const isPhrasesFilter = (filter: any): filter is PhrasesFilter => - filter && filter.meta.type === 'phrases'; +export { useValidation }; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx index 8e3f66d12b9bdf..ed3b52b83c2345 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx @@ -21,13 +21,12 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; -import { BasicOptions, SwitchOption } from '../../common'; +import { BasicOptions, SwitchOption, ValidationVisOptionsProps } from '../../common'; import { GridPanel } from './grid_panel'; import { ThresholdPanel } from './threshold_panel'; import { BasicVislibParams } from '../../../types'; -function PointSeriesOptions(props: VisOptionsProps) { +function PointSeriesOptions(props: ValidationVisOptionsProps) { const { stateParams, setValue, vis } = props; return ( diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx index 49e56e377a8d56..591ad2eb3a001a 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx @@ -21,11 +21,16 @@ import { EuiPanel, EuiTitle, EuiColorPicker, EuiFormRow, EuiSpacer } from '@elas import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; -import { NumberInputOption, SelectOption, SwitchOption } from '../../common'; +import { SelectOption, SwitchOption, ValidationVisOptionsProps } from '../../common'; +import { NumberInputOption } from '../../common/required_number_input'; import { BasicVislibParams } from '../../../types'; -function ThresholdPanel({ stateParams, setValue, vis }: VisOptionsProps) { +function ThresholdPanel({ + stateParams, + setValue, + setMultipleValidity, + vis, +}: ValidationVisOptionsProps) { const setThresholdLine = useCallback( ( paramName: T, @@ -39,6 +44,12 @@ function ThresholdPanel({ stateParams, setValue, vis }: VisOptionsProps + setMultipleValidity(`thresholdLine__${paramName}`, isValid), + [setMultipleValidity] + ); + return ( @@ -72,6 +83,7 @@ function ThresholdPanel({ stateParams, setValue, vis }: VisOptionsProps ) => ( + + ), }, ]; } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 7a0398e86a60da..5fa3a938ed9dfc 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -35,7 +35,6 @@ import { } from 'ui/state_management/app_state'; import { KbnUrl } from 'ui/url/kbn_url'; -import { Filter } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/public'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; @@ -46,6 +45,7 @@ import { Subscription } from 'rxjs'; import { ViewMode } from '../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types'; +import { esFilters } from '../../../../../../src/plugins/data/public'; import { DashboardAppController } from './dashboard_app_controller'; @@ -55,7 +55,7 @@ export interface DashboardAppScope extends ng.IScope { screenTitle: string; model: { query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; timeRestore: boolean; title: string; description: string; @@ -81,9 +81,9 @@ export interface DashboardAppScope extends ng.IScope { isPaused: boolean; refreshInterval: any; }) => void; - onFiltersUpdated: (filters: Filter[]) => void; + onFiltersUpdated: (filters: esFilters.Filter[]) => void; onCancelApplyFilters: () => void; - onApplyFilters: (filters: Filter[]) => void; + onApplyFilters: (filters: esFilters.Filter[]) => void; onQuerySaved: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; onClearSavedQuery: () => void; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index abf7b22a6e48c6..64c75609476813 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -48,7 +48,6 @@ import { } from 'ui/state_management/app_state'; import { KbnUrl } from 'ui/url/kbn_url'; -import { Filter } from '@kbn/es-query'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; @@ -59,6 +58,7 @@ import { npStart } from 'ui/new_platform'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; import { extractTimeFilter, changeTimeFilter } from '../../../data/public'; import { start as data } from '../../../data/public/legacy'; +import { esFilters } from '../../../../../plugins/data/public'; import { DashboardContainer, @@ -514,7 +514,7 @@ export class DashboardAppController { } ); - $scope.$watch('appState.$newFilters', (filters: Filter[] = []) => { + $scope.$watch('appState.$newFilters', (filters: esFilters.Filter[] = []) => { if (filters.length === 1) { $scope.onApplyFilters(filters); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 7c1fc771de3491..8ffabe5add1c34 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -20,7 +20,6 @@ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { Filter } from '@kbn/es-query'; import { stateMonitorFactory, StateMonitor } from 'ui/state_management/state_monitor_factory'; import { Timefilter } from 'ui/timefilter'; import { AppStateClass as TAppStateClass } from 'ui/state_management/app_state'; @@ -29,6 +28,7 @@ import { Moment } from 'moment'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; +import { esFilters } from '../../../../../../src/plugins/data/public'; import { Query } from '../../../data/public'; import { getAppStateDefaults, migrateAppState } from './lib'; @@ -50,7 +50,7 @@ export class DashboardStateManager { public lastSavedDashboardFilters: { timeTo?: string | Moment; timeFrom?: string | Moment; - filterBars: Filter[]; + filterBars: esFilters.Filter[]; query: Query; }; private stateDefaults: DashboardAppStateDefaults; @@ -303,7 +303,7 @@ export class DashboardStateManager { return this.savedDashboard.timeRestore; } - public getLastSavedFilterBars(): Filter[] { + public getLastSavedFilterBars(): esFilters.Filter[] { return this.lastSavedDashboardFilters.filterBars; } @@ -461,7 +461,7 @@ export class DashboardStateManager { * Applies the current filter state to the dashboard. * @param filter An array of filter bar filters. */ - public applyFilters(query: Query, filters: Filter[]) { + public applyFilters(query: Query, filters: esFilters.Filter[]) { this.appState.query = query; this.savedDashboard.searchSource.setField('query', query); this.savedDashboard.searchSource.setField('filter', filters); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/filter_utils.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/filter_utils.ts index 1fd50081c58bd6..19a0c32210737b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/filter_utils.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/filter_utils.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; import moment, { Moment } from 'moment'; -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; /** * @typedef {Object} QueryFilter @@ -65,9 +65,9 @@ export class FilterUtils { * @param filters {Array.} * @returns {Array.} */ - public static cleanFiltersForComparison(filters: Filter[]) { + public static cleanFiltersForComparison(filters: esFilters.Filter[]) { return _.map(filters, filter => { - const f: Partial = _.omit(filter, ['$$hashKey', '$state']); + const f: Partial = _.omit(filter, ['$$hashKey', '$state']); if (f.meta) { // f.meta.value is the value displayed in the filter bar. // It may also be loaded differently and shouldn't be used in this comparison. diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts index 1f503ee6754070..ae3edae3b85d61 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.test.ts @@ -18,12 +18,12 @@ */ import { moveFiltersToQuery, Pre600FilterQuery } from './move_filters_to_query'; -import { Filter, FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; -const filter: Filter = { +const filter: esFilters.Filter = { meta: { disabled: false, negate: false, alias: '' }, query: {}, - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, }; const queryFilter: Pre600FilterQuery = { @@ -38,7 +38,7 @@ test('Migrates an old filter query into the query field', () => { expect(newSearchSource).toEqual({ filter: [ { - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { alias: '', disabled: false, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts index 153bdeba9d35f1..8522495b9dedb2 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/move_filters_to_query.ts @@ -18,7 +18,7 @@ */ import { Query } from 'src/legacy/core_plugins/data/public'; -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; export interface Pre600FilterQuery { // pre 6.0.0 global query:queryString:options were stored per dashboard and would @@ -30,18 +30,18 @@ export interface Pre600FilterQuery { export interface SearchSourcePre600 { // I encountered at least one export from 7.0.0-alpha that was missing the filter property in here. // The maps data in esarchives actually has it, but I don't know how/when they created it. - filter?: Array; + filter?: Array; } export interface SearchSource730 { - filter: Filter[]; + filter: esFilters.Filter[]; query: Query; highlightAll?: boolean; version?: boolean; } -function isQueryFilter(filter: Filter | { query: unknown }): filter is Pre600FilterQuery { - return filter.query && !(filter as Filter).meta; +function isQueryFilter(filter: esFilters.Filter | { query: unknown }): filter is Pre600FilterQuery { + return filter.query && !(filter as esFilters.Filter).meta; } export function moveFiltersToQuery( diff --git a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts index 1231ca28ed014b..5b860b0a2cc7c1 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.d.ts @@ -19,10 +19,9 @@ import { SearchSource } from 'ui/courier'; import { SavedObject } from 'ui/saved_objects/saved_object'; -import moment from 'moment'; import { RefreshInterval } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; export interface SavedObjectDashboard extends SavedObject { id?: string; @@ -41,5 +40,5 @@ export interface SavedObjectDashboard extends SavedObject { destroy: () => void; refreshInterval?: RefreshInterval; getQuery(): Query; - getFilters(): Filter[]; + getFilters(): esFilters.Filter[]; } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index ccccc89004e365..5aaca7b62094f8 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -18,7 +18,6 @@ */ import { AppState } from 'ui/state_management/app_state'; -import { Filter } from '@kbn/es-query'; import { Query } from 'src/legacy/core_plugins/data/public'; import { AppState as TAppState } from 'ui/state_management/app_state'; import { ViewMode } from 'src/plugins/embeddable/public'; @@ -30,6 +29,7 @@ import { RawSavedDashboardPanel640To720, RawSavedDashboardPanel730ToLatest, } from './migrations/types'; +import { esFilters } from '../../../../../plugins/data/public'; export type NavAction = (anchorElement?: any) => void; @@ -110,7 +110,7 @@ export interface DashboardAppStateParameters { useMargins: boolean; }; query: Query | string; - filters: Filter[]; + filters: esFilters.Filter[]; viewMode: ViewMode; savedQuery?: string; } diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts index 268f176f2c61e1..3314bbbf189c4b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context/api/context.ts @@ -17,7 +17,6 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { IndexPatterns, IndexPattern, getServices } from '../../../kibana_services'; import { reverseSortDir, SortDirection } from './utils/sorting'; import { extractNanos, convertIsoToMillis } from './utils/date_conversion'; @@ -25,6 +24,7 @@ import { fetchHitsInInterval } from './utils/fetch_hits_in_interval'; import { generateIntervals } from './utils/generate_intervals'; import { getEsQuerySearchAfter } from './utils/get_es_query_search_after'; import { getEsQuerySort } from './utils/get_es_query_sort'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export type SurrDocType = 'successors' | 'predecessors'; export interface EsHitRecord { @@ -67,7 +67,7 @@ function fetchContextProvider(indexPatterns: IndexPatterns) { tieBreakerField: string, sortDir: SortDirection, size: number, - filters: Filter[] + filters: esFilters.Filter[] ) { if (typeof anchor !== 'object' || anchor === null) { return []; @@ -112,7 +112,7 @@ function fetchContextProvider(indexPatterns: IndexPatterns) { return documents; } - async function createSearchSource(indexPattern: IndexPattern, filters: Filter[]) { + async function createSearchSource(indexPattern: IndexPattern, filters: esFilters.Filter[]) { return new SearchSource() .setParent(false) .setField('index', indexPattern) diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js index 355d9defbb63d6..6f5a94442e9774 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js +++ b/src/legacy/core_plugins/kibana/public/discover/angular/doc_table/components/table_row.js @@ -26,10 +26,10 @@ import { noWhiteSpace } from '../../../../../common/utils/no_white_space'; import openRowHtml from './table_row/open.html'; import detailsHtml from './table_row/details.html'; import { getServices } from '../../../kibana_services'; -import { disableFilter } from '@kbn/es-query'; import { dispatchRenderComplete } from '../../../../../../../../plugins/kibana_utils/public'; import cellTemplateHtml from '../components/table_row/cell.html'; import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html'; +import { esFilters } from '../../../../../../../../plugins/data/public'; const module = getServices().uiModules.get('app/discover'); @@ -117,7 +117,7 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl const hash = $httpParamSerializer({ _a: rison.encode({ columns: $scope.columns, - filters: ($scope.filters || []).map(disableFilter), + filters: ($scope.filters || []).map(esFilters.disableFilter), }), }); return `${path}?${hash}`; diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/_field_chooser.scss b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/_field_chooser.scss index 6960c7101fa10a..fe13ac2fafa01f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/_field_chooser.scss +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/_field_chooser.scss @@ -27,3 +27,11 @@ padding-left: $euiSizeXS; margin-left: $euiSizeXS; } + +.dscFieldSearch__filterWrapper { + flex-grow: 0; +} + +.dscFieldSearch__formWrapper { + padding: $euiSizeM; +} diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx index 2655d28af985bc..c207585499483f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.test.tsx @@ -17,54 +17,123 @@ * under the License. */ import React from 'react'; +import { act } from 'react-dom/test-utils'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; -import { DiscoverFieldSearch } from './discover_field_search'; +import { DiscoverFieldSearch, Props } from './discover_field_search'; +import { EuiButtonGroupProps } from '@elastic/eui'; +import { ReactWrapper } from 'enzyme'; describe('DiscoverFieldSearch', () => { - function mountComponent() { - const props = { - onChange: jest.fn(), - onShowFilter: jest.fn(), - showFilter: false, - filtersActive: 0, - value: 'test', - }; - const comp = mountWithIntl(); - const input = findTestSubject(comp, 'fieldFilterSearchInput'); - const btn = findTestSubject(comp, 'toggleFieldFilterButton'); - return { comp, input, btn, props }; + const defaultProps = { + onChange: jest.fn(), + value: 'test', + types: ['any', 'string', '_source'], + }; + + function mountComponent(props?: Props) { + const compProps = props || defaultProps; + const comp = mountWithIntl(); + return comp; + } + + function findButtonGroup(component: ReactWrapper, id: string) { + return component.find(`[data-test-subj="${id}ButtonGroup"]`).first(); } test('enter value', () => { - const { input, props } = mountComponent(); + const component = mountComponent(); + const input = findTestSubject(component, 'fieldFilterSearchInput'); input.simulate('change', { target: { value: 'new filter' } }); - expect(props.onChange).toBeCalledTimes(1); + expect(defaultProps.onChange).toBeCalledTimes(1); }); - // this should work, but doesn't, have to do some research - test('click toggle filter button', () => { - const { btn, props } = mountComponent(); + test('change in active filters should change facet selection and call onChange', () => { + const onChange = jest.fn(); + const component = mountComponent({ ...defaultProps, ...{ onChange } }); + let btn = findTestSubject(component, 'toggleFieldFilterButton'); + expect(btn.hasClass('euiFacetButton--isSelected')).toBeFalsy(); btn.simulate('click'); - expect(props.onShowFilter).toBeCalledTimes(1); + const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); + act(() => { + // @ts-ignore + (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); + }); + component.update(); + btn = findTestSubject(component, 'toggleFieldFilterButton'); + expect(btn.hasClass('euiFacetButton--isSelected')).toBe(true); + expect(onChange).toBeCalledWith('aggregatable', true); }); - test('change showFilter value should change aria label', () => { - const { comp } = mountComponent(); - let btn = findTestSubject(comp, 'toggleFieldFilterButton'); - expect(btn.prop('aria-label')).toEqual('Show field filter settings'); - comp.setProps({ showFilter: true }); - btn = findTestSubject(comp, 'toggleFieldFilterButton'); - expect(btn.prop('aria-label')).toEqual('Hide field filter settings'); + test('change in active filters should change filters count', () => { + const component = mountComponent(); + let btn = findTestSubject(component, 'toggleFieldFilterButton'); + btn.simulate('click'); + btn = findTestSubject(component, 'toggleFieldFilterButton'); + const badge = btn.find('.euiNotificationBadge'); + // no active filters + expect(badge.text()).toEqual('0'); + // change value of aggregatable select + const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); + act(() => { + // @ts-ignore + (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); + }); + component.update(); + expect(badge.text()).toEqual('1'); + // change value of searchable select + const searchableButtonGroup = findButtonGroup(component, 'searchable'); + act(() => { + // @ts-ignore + (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-true', null); + }); + component.update(); + expect(badge.text()).toEqual('2'); + // change value of searchable select + act(() => { + // @ts-ignore + (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-any', null); + }); + component.update(); + expect(badge.text()).toEqual('1'); }); - test('change filtersActive should change facet selection', () => { - const { comp } = mountComponent(); - let btn = findTestSubject(comp, 'toggleFieldFilterButton'); - expect(btn.hasClass('euiFacetButton--isSelected')).toBeFalsy(); - comp.setProps({ filtersActive: 3 }); - btn = findTestSubject(comp, 'toggleFieldFilterButton'); - expect(btn.hasClass('euiFacetButton--isSelected')).toBe(true); + test('change in missing fields switch should not change filter count', () => { + const component = mountComponent(); + const btn = findTestSubject(component, 'toggleFieldFilterButton'); + btn.simulate('click'); + const badge = btn.find('.euiNotificationBadge'); + expect(badge.text()).toEqual('0'); + const missingSwitch = findTestSubject(component, 'missingSwitch'); + missingSwitch.simulate('change', { target: { value: false } }); + expect(badge.text()).toEqual('0'); + }); + + test('change in filters triggers onChange', () => { + const onChange = jest.fn(); + const component = mountComponent({ ...defaultProps, ...{ onChange } }); + const btn = findTestSubject(component, 'toggleFieldFilterButton'); + btn.simulate('click'); + const aggregtableButtonGroup = findButtonGroup(component, 'aggregatable'); + const missingSwitch = findTestSubject(component, 'missingSwitch'); + act(() => { + // @ts-ignore + (aggregtableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); + }); + missingSwitch.simulate('change', { target: { value: false } }); + expect(onChange).toBeCalledTimes(2); + }); + + test('change in type filters triggers onChange with appropriate value', () => { + const onChange = jest.fn(); + const component = mountComponent({ ...defaultProps, ...{ onChange } }); + const btn = findTestSubject(component, 'toggleFieldFilterButton'); + btn.simulate('click'); + const typeSelector = findTestSubject(component, 'typeSelect'); + typeSelector.simulate('change', { target: { value: 'string' } }); + expect(onChange).toBeCalledWith('type', 'string'); + typeSelector.simulate('change', { target: { value: 'any' } }); + expect(onChange).toBeCalledWith('type', 'any'); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx index 1577b4deece516..f0685c4357c5af 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search.tsx @@ -16,61 +16,234 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; +import React, { OptionHTMLAttributes, ReactNode, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFacetButton, EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { + EuiFacetButton, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + EuiPopoverFooter, + EuiPopoverTitle, + EuiSelect, + EuiSwitch, + EuiForm, + EuiFormRow, + EuiButtonGroup, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +export interface State { + searchable: string; + aggregatable: string; + type: string; + missing: boolean; + [index: string]: string | boolean; +} + export interface Props { /** * triggered on input of user into search field */ - onChange: (field: string, value: string) => void; - /** - * triggered when the "additional filter btn" is clicked - */ - onShowFilter: () => void; - /** - * determines whether additional filter fields are displayed - */ - showFilter: boolean; + onChange: (field: string, value: string | boolean | undefined) => void; + /** * the input value of the user */ value?: string; + /** - * the number of selected filters + * types for the type filter */ - filtersActive: number; + types: string[]; } /** * Component is Discover's side bar to search of available fields * Additionally there's a button displayed that allows the user to show/hide more filter fields */ -export function DiscoverFieldSearch({ - showFilter, - onChange, - onShowFilter, - value, - filtersActive, -}: Props) { +export function DiscoverFieldSearch({ onChange, value, types }: Props) { if (typeof value !== 'string') { // at initial rendering value is undefined (angular related), this catches the warning // should be removed once all is react return null; } - const filterBtnAriaLabel = showFilter + const searchPlaceholder = i18n.translate('kbn.discover.fieldChooser.searchPlaceHolder', { + defaultMessage: 'Search field names', + }); + const aggregatableLabel = i18n.translate('kbn.discover.fieldChooser.filter.aggregatableLabel', { + defaultMessage: 'Aggregatable', + }); + const searchableLabel = i18n.translate('kbn.discover.fieldChooser.filter.searchableLabel', { + defaultMessage: 'Searchable', + }); + const typeLabel = i18n.translate('kbn.discover.fieldChooser.filter.typeLabel', { + defaultMessage: 'Type', + }); + const typeOptions = types + ? types.map(type => { + return { value: type, text: type }; + }) + : [{ value: 'any', text: 'any' }]; + + const [activeFiltersCount, setActiveFiltersCount] = useState(0); + const [isPopoverOpen, setPopoverOpen] = useState(false); + const [values, setValues] = useState({ + searchable: 'any', + aggregatable: 'any', + type: 'any', + missing: true, + }); + + const filterBtnAriaLabel = isPopoverOpen ? i18n.translate('kbn.discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel', { defaultMessage: 'Hide field filter settings', }) : i18n.translate('kbn.discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel', { defaultMessage: 'Show field filter settings', }); - const searchPlaceholder = i18n.translate('kbn.discover.fieldChooser.searchPlaceHolder', { - defaultMessage: 'Search field names', - }); + const handleFacetButtonClicked = () => { + setPopoverOpen(!isPopoverOpen); + }; + + const applyFilterValue = (id: string, filterValue: string | boolean) => { + switch (filterValue) { + case 'any': + if (id !== 'type') { + onChange(id, undefined); + } else { + onChange(id, filterValue); + } + break; + case 'true': + onChange(id, true); + break; + case 'false': + onChange(id, false); + break; + default: + onChange(id, filterValue); + } + }; + + const isFilterActive = (name: string, filterValue: string | boolean) => { + return name !== 'missing' && filterValue !== 'any'; + }; + + const handleValueChange = (name: string, filterValue: string | boolean) => { + const previousValue = values[name]; + updateFilterCount(name, previousValue, filterValue); + const updatedValues = { ...values }; + updatedValues[name] = filterValue; + setValues(updatedValues); + applyFilterValue(name, filterValue); + }; + + const updateFilterCount = ( + name: string, + previousValue: string | boolean, + currentValue: string | boolean + ) => { + const previouslyFilterActive = isFilterActive(name, previousValue); + const filterActive = isFilterActive(name, currentValue); + const diff = Number(filterActive) - Number(previouslyFilterActive); + setActiveFiltersCount(activeFiltersCount + diff); + }; + + const handleMissingChange = (e: React.ChangeEvent) => { + const missingValue = e.target.checked; + handleValueChange('missing', missingValue); + }; + + const buttonContent = ( + } + isSelected={activeFiltersCount > 0} + quantity={activeFiltersCount} + onClick={handleFacetButtonClicked} + > + + + ); + + const select = ( + id: string, + selectOptions: Array<{ text: ReactNode } & OptionHTMLAttributes>, + selectValue: string + ) => { + return ( + ) => + handleValueChange(id, e.target.value) + } + aria-label={i18n.translate('kbn.discover.fieldChooser.filter.fieldSelectorLabel', { + defaultMessage: 'Selection of {id} filter options', + values: { id }, + })} + data-test-subj={`${id}Select`} + compressed + /> + ); + }; + + const toggleButtons = (id: string) => { + return [ + { + id: `${id}-any`, + label: 'any', + }, + { + id: `${id}-true`, + label: 'yes', + }, + { + id: `${id}-false`, + label: 'no', + }, + ]; + }; + + const buttonGroup = (id: string, legend: string) => { + return ( + handleValueChange(id, optionId.replace(`${id}-`, ''))} + buttonSize="compressed" + isFullWidth + data-test-subj={`${id}ButtonGroup`} + /> + ); + }; + + const selectionPanel = ( +
+ + + {buttonGroup('aggregatable', aggregatableLabel)} + + + {buttonGroup('searchable', searchableLabel)} + + + {select('type', typeOptions, values.type)} + + +
+ ); return ( @@ -86,20 +259,35 @@ export function DiscoverFieldSearch({ /> - } - isSelected={filtersActive > 0} - quantity={filtersActive} - onClick={() => onShowFilter()} - > - - +
+ {}} + button={buttonContent} + > + + {i18n.translate('kbn.discover.fieldChooser.filter.filterByTypeLabel', { + defaultMessage: 'Filter by type', + })} + + {selectionPanel} + + + + +
); } diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts index 2e7dd3e210ef8e..b78f993e187725 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/discover_field_search_directive.ts @@ -27,9 +27,7 @@ const app = uiModules.get('apps/discover'); app.directive('discoverFieldSearch', function(reactDirective: any) { return reactDirective(wrapInI18nContext(DiscoverFieldSearch), [ ['onChange', { watchDepth: 'reference' }], - ['onShowFilter', { watchDepth: 'reference' }], - ['showFilter', { watchDepth: 'value' }], ['value', { watchDepth: 'value' }], - ['filtersActive', { watchDepth: 'value' }], + ['types', { watchDepth: 'value' }], ]); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html index 3d8b38c278f318..adf4b1b4326e88 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html @@ -9,10 +9,8 @@
diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index e777501d35ca09..31b28d21fe8d84 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -19,12 +19,12 @@ import _ from 'lodash'; import * as Rx from 'rxjs'; import { Subscription } from 'rxjs'; -import { Filter, FilterStateStore } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public'; import { setup as data } from '../../../../data/public/legacy'; import { Query, getTime } from '../../../../data/public'; +import { esFilters } from '../../../../../../plugins/data/public'; import { APPLY_FILTER_TRIGGER, Container, @@ -75,7 +75,7 @@ export interface FilterManager { values: string | string[], operation: string, index: number - ) => Filter[]; + ) => esFilters.Filter[]; } interface SearchEmbeddableConfig { @@ -105,7 +105,7 @@ export class SearchEmbeddable extends Embeddable private abortController?: AbortController; private prevTimeRange?: TimeRange; - private prevFilters?: Filter[]; + private prevFilters?: esFilters.Filter[]; private prevQuery?: Query; constructor( @@ -248,7 +248,7 @@ export class SearchEmbeddable extends Embeddable let filters = this.filterGen.generate(field, value, operator, indexPattern.id); filters = filters.map(filter => ({ ...filter, - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, })); await this.executeTriggerActions(APPLY_FILTER_TRIGGER, { diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts index db8d2afc7aff3a..5473ec0e7b8b4d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts @@ -19,16 +19,16 @@ import { TimeRange } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; -import { Filter } from '@kbn/es-query'; import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from 'src/plugins/embeddable/public'; import { StaticIndexPattern } from '../kibana_services'; import { SavedSearch } from '../types'; import { SortOrder } from '../angular/doc_table/components/table_header/helpers'; +import { esFilters } from '../../../../../../plugins/data/public'; export interface SearchInput extends EmbeddableInput { timeRange: TimeRange; query?: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; hidePanelTitles?: boolean; columns?: string[]; sort?: SortOrder[]; diff --git a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/home.test.js.snap b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/home.test.js.snap index fcf60a6009b29c..71c336b1d48d20 100644 --- a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/home.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/home.test.js.snap @@ -2,6 +2,7 @@ exports[`home directories should not render directory entry when showOnHomePage is false 1`] = ` + diff --git a/src/legacy/core_plugins/kibana/public/home/components/home_app.js b/src/legacy/core_plugins/kibana/public/home/components/home_app.js index 005d4bdb0a99eb..e4a6753e0771a2 100644 --- a/src/legacy/core_plugins/kibana/public/home/components/home_app.js +++ b/src/legacy/core_plugins/kibana/public/home/components/home_app.js @@ -18,21 +18,16 @@ */ import React from 'react'; +import { I18nProvider } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; import { Home } from './home'; import { FeatureDirectory } from './feature_directory'; import { TutorialDirectory } from './tutorial_directory'; import { Tutorial } from './tutorial/tutorial'; -import { - HashRouter as Router, - Switch, - Route, -} from 'react-router-dom'; +import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; import { getTutorial } from '../load_tutorials'; import { replaceTemplateStrings } from './tutorial/replace_template_strings'; -import { - getServices -} from '../kibana_services'; +import { getServices } from '../kibana_services'; export function HomeApp({ directories }) { const { @@ -47,8 +42,9 @@ export function HomeApp({ directories }) { const isCloudEnabled = getInjected('isCloudEnabled', false); const apmUiEnabled = getInjected('apmUiEnabled', true); const mlEnabled = getInjected('mlEnabled', false); + const defaultAppId = getInjected('kbnDefaultAppId', 'discover'); - const renderTutorialDirectory = (props) => { + const renderTutorialDirectory = props => { return ( { + const renderTutorial = props => { return ( - - - - - - - - - - - + + + + + + + + + + + + + + + + + ); } HomeApp.propTypes = { - directories: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - icon: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - showOnHomePage: PropTypes.bool.isRequired, - category: PropTypes.string.isRequired, - })), + directories: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, + showOnHomePage: PropTypes.bool.isRequired, + category: PropTypes.string.isRequired, + }) + ), }; diff --git a/src/legacy/core_plugins/kibana/public/home/home_ng_wrapper.html b/src/legacy/core_plugins/kibana/public/home/home_ng_wrapper.html deleted file mode 100644 index 645855766fab8e..00000000000000 --- a/src/legacy/core_plugins/kibana/public/home/home_ng_wrapper.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/src/legacy/core_plugins/kibana/public/home/index.js b/src/legacy/core_plugins/kibana/public/home/index.js deleted file mode 100644 index 01f94b8ee4368b..00000000000000 --- a/src/legacy/core_plugins/kibana/public/home/index.js +++ /dev/null @@ -1,61 +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. - */ - -import { getServices } from './kibana_services'; -import template from './home_ng_wrapper.html'; -import { - HomeApp -} from './components/home_app'; -import { i18n } from '@kbn/i18n'; - -const { wrapInI18nContext, uiRoutes, uiModules } = getServices(); - -const app = uiModules.get('apps/home', []); -app.directive('homeApp', function (reactDirective) { - return reactDirective(wrapInI18nContext(HomeApp)); -}); - -const homeTitle = i18n.translate('kbn.home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); - -function getRoute() { - return { - template, - resolve: { - directories: () => getServices().getFeatureCatalogueEntries() - }, - controller($scope, $route) { - const { chrome, addBasePath } = getServices(); - $scope.directories = $route.current.locals.directories; - $scope.recentlyAccessed = chrome.recentlyAccessed.get().map(item => { - item.link = addBasePath(item.link); - return item; - }); - }, - k7Breadcrumbs: () => [ - { text: homeTitle }, - ] - }; -} - -// All routing will be handled inside HomeApp via react, we just need to make sure angular doesn't -// redirect us to the default page by encountering a url it isn't marked as being able to handle. -uiRoutes.when('/home', getRoute()); -uiRoutes.when('/home/feature_directory', getRoute()); -uiRoutes.when('/home/tutorial_directory/:tab?', getRoute()); -uiRoutes.when('/home/tutorial/:id', getRoute()); diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts new file mode 100644 index 00000000000000..4ebf719b86233a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/index.ts @@ -0,0 +1,80 @@ +/* + * 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 { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; +import { npSetup, npStart } from 'ui/new_platform'; +import chrome from 'ui/chrome'; +import { IPrivate } from 'ui/private'; +import { HomePlugin, LegacyAngularInjectedDependencies } from './plugin'; +import { createUiStatsReporter, METRIC_TYPE } from '../../../ui_metric/public'; +import { start as data } from '../../../data/public/legacy'; +import { TelemetryOptInProvider } from '../../../telemetry/public/services'; +import { localApplicationService } from '../local_application_service'; + +export const trackUiMetric = createUiStatsReporter('Kibana_home'); + +/** + * Get dependencies relying on the global angular context. + * They also have to get resolved together with the legacy imports above + */ +async function getAngularDependencies(): Promise { + const injector = await chrome.dangerouslyGetActiveInjector(); + + const Private = injector.get('Private'); + + const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); + const telemetryBanner = npStart.core.injectedMetadata.getInjectedVar('telemetryBanner'); + const telemetryOptInProvider = Private(TelemetryOptInProvider); + + return { + telemetryOptInProvider, + shouldShowTelemetryOptIn: + telemetryEnabled && telemetryBanner && !telemetryOptInProvider.getOptIn(), + }; +} + +let copiedLegacyCatalogue = false; + +(async () => { + const instance = new HomePlugin(); + instance.setup(npSetup.core, { + __LEGACY: { + trackUiMetric, + metadata: npStart.core.injectedMetadata.getLegacyMetadata(), + METRIC_TYPE, + getFeatureCatalogueEntries: async () => { + if (!copiedLegacyCatalogue) { + const injector = await chrome.dangerouslyGetActiveInjector(); + const Private = injector.get('Private'); + // Merge legacy registry with new registry + (Private(FeatureCatalogueRegistryProvider as any) as any).inTitleOrder.map( + npSetup.plugins.feature_catalogue.register + ); + copiedLegacyCatalogue = true; + } + return npStart.plugins.feature_catalogue.get(); + }, + getAngularDependencies, + localApplicationService, + }, + }); + instance.start(npStart.core, { + data, + }); +})(); diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts index b9f2ae1cfa7e8e..6189204ee4cfc9 100644 --- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts @@ -17,69 +17,63 @@ * under the License. */ -// @ts-ignore -import { toastNotifications, banners } from 'ui/notify'; -import { kfetch } from 'ui/kfetch'; -import chrome from 'ui/chrome'; +import { + ChromeStart, + DocLinksStart, + HttpStart, + LegacyNavLink, + NotificationsSetup, + OverlayStart, + SavedObjectsClientContract, + UiSettingsClientContract, + UiSettingsState, +} from 'kibana/public'; +import { UiStatsMetricType } from '@kbn/analytics'; +import { FeatureCatalogueEntry } from '../../../../../plugins/feature_catalogue/public'; -import { wrapInI18nContext } from 'ui/i18n'; +export interface HomeKibanaServices { + indexPatternService: any; + getFeatureCatalogueEntries: () => Promise; + metadata: { + app: unknown; + bundleId: string; + nav: LegacyNavLink[]; + version: string; + branch: string; + buildNum: number; + buildSha: string; + basePath: string; + serverName: string; + devMode: boolean; + uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; + }; + getInjected: (name: string, defaultValue?: any) => unknown; + chrome: ChromeStart; + telemetryOptInProvider: any; + uiSettings: UiSettingsClientContract; + http: HttpStart; + savedObjectsClient: SavedObjectsClientContract; + toastNotifications: NotificationsSetup['toasts']; + banners: OverlayStart['banners']; + METRIC_TYPE: any; + trackUiMetric: (type: UiStatsMetricType, eventNames: string | string[], count?: number) => void; + getBasePath: () => string; + shouldShowTelemetryOptIn: boolean; + docLinks: DocLinksStart; + addBasePath: (url: string) => string; +} -// @ts-ignore -import { uiModules as modules } from 'ui/modules'; -import routes from 'ui/routes'; -import { npSetup, npStart } from 'ui/new_platform'; -import { IPrivate } from 'ui/private'; -import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; -import { createUiStatsReporter, METRIC_TYPE } from '../../../ui_metric/public'; -import { TelemetryOptInProvider } from '../../../telemetry/public/services'; -import { start as data } from '../../../data/public/legacy'; +let services: HomeKibanaServices | null = null; -let shouldShowTelemetryOptIn: boolean; -let telemetryOptInProvider: any; +export function setServices(newServices: HomeKibanaServices) { + services = newServices; +} export function getServices() { - return { - getInjected: npStart.core.injectedMetadata.getInjectedVar, - metadata: npStart.core.injectedMetadata.getLegacyMetadata(), - docLinks: npStart.core.docLinks, - - uiRoutes: routes, - uiModules: modules, - - savedObjectsClient: npStart.core.savedObjects.client, - chrome: npStart.core.chrome, - uiSettings: npStart.core.uiSettings, - addBasePath: npStart.core.http.basePath.prepend, - getBasePath: npStart.core.http.basePath.get, - - indexPatternService: data.indexPatterns.indexPatterns, - shouldShowTelemetryOptIn, - telemetryOptInProvider, - getFeatureCatalogueEntries: async () => { - const injector = await chrome.dangerouslyGetActiveInjector(); - const Private = injector.get('Private'); - // Merge legacy registry with new registry - (Private(FeatureCatalogueRegistryProvider as any) as any).inTitleOrder.map( - npSetup.plugins.feature_catalogue.register - ); - return npStart.plugins.feature_catalogue.get(); - }, - - trackUiMetric: createUiStatsReporter('Kibana_home'), - METRIC_TYPE, - - toastNotifications, - banners, - kfetch, - wrapInI18nContext, - }; + if (!services) { + throw new Error( + 'Kibana services not set - are you trying to import this module from outside of the home app?' + ); + } + return services; } - -modules.get('kibana').run((Private: IPrivate) => { - const telemetryEnabled = npStart.core.injectedMetadata.getInjectedVar('telemetryEnabled'); - const telemetryBanner = npStart.core.injectedMetadata.getInjectedVar('telemetryBanner'); - - telemetryOptInProvider = Private(TelemetryOptInProvider); - shouldShowTelemetryOptIn = - telemetryEnabled && telemetryBanner && !telemetryOptInProvider.getOptIn(); -}); diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts new file mode 100644 index 00000000000000..2a2ea371d7f3bd --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, CoreStart, LegacyNavLink, Plugin, UiSettingsState } from 'kibana/public'; +import { UiStatsMetricType } from '@kbn/analytics'; + +import { DataStart } from '../../../data/public'; +import { LocalApplicationService } from '../local_application_service'; +import { setServices } from './kibana_services'; +import { FeatureCatalogueEntry } from '../../../../../plugins/feature_catalogue/public'; + +export interface LegacyAngularInjectedDependencies { + telemetryOptInProvider: any; + shouldShowTelemetryOptIn: boolean; +} + +export interface HomePluginStartDependencies { + data: DataStart; +} + +export interface HomePluginSetupDependencies { + __LEGACY: { + trackUiMetric: (type: UiStatsMetricType, eventNames: string | string[], count?: number) => void; + METRIC_TYPE: any; + metadata: { + app: unknown; + bundleId: string; + nav: LegacyNavLink[]; + version: string; + branch: string; + buildNum: number; + buildSha: string; + basePath: string; + serverName: string; + devMode: boolean; + uiSettings: { defaults: UiSettingsState; user?: UiSettingsState | undefined }; + }; + getFeatureCatalogueEntries: () => Promise; + getAngularDependencies: () => Promise; + localApplicationService: LocalApplicationService; + }; +} + +export class HomePlugin implements Plugin { + private dataStart: DataStart | null = null; + private savedObjectsClient: any = null; + + setup( + core: CoreSetup, + { + __LEGACY: { localApplicationService, getAngularDependencies, ...legacyServices }, + }: HomePluginSetupDependencies + ) { + localApplicationService.register({ + id: 'home', + title: 'Home', + mount: async ({ core: contextCore }, params) => { + const angularDependencies = await getAngularDependencies(); + setServices({ + ...legacyServices, + http: contextCore.http, + toastNotifications: core.notifications.toasts, + banners: contextCore.overlays.banners, + getInjected: core.injectedMetadata.getInjectedVar, + docLinks: contextCore.docLinks, + savedObjectsClient: this.savedObjectsClient!, + chrome: contextCore.chrome, + uiSettings: core.uiSettings, + addBasePath: core.http.basePath.prepend, + getBasePath: core.http.basePath.get, + indexPatternService: this.dataStart!.indexPatterns.indexPatterns, + ...angularDependencies, + }); + const { renderApp } = await import('./render_app'); + return await renderApp(params.element); + }, + }); + } + + start(core: CoreStart, { data }: HomePluginStartDependencies) { + // TODO is this really the right way? I though the app context would give us those + this.dataStart = data; + this.savedObjectsClient = core.savedObjects.client; + } + + stop() {} +} diff --git a/src/legacy/core_plugins/kibana/public/home/render_app.tsx b/src/legacy/core_plugins/kibana/public/home/render_app.tsx new file mode 100644 index 00000000000000..a8c35144a45b01 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/home/render_app.tsx @@ -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 React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { HomeApp } from './components/home_app'; +import { getServices } from './kibana_services'; + +export const renderApp = async (element: HTMLElement) => { + const homeTitle = i18n.translate('kbn.home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); + const { getFeatureCatalogueEntries, chrome } = getServices(); + const directories = await getFeatureCatalogueEntries(); + chrome.setBreadcrumbs([{ text: homeTitle }]); + + render(, element); + + return () => { + unmountComponentAtNode(element); + }; +}; diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_client.js b/src/legacy/core_plugins/kibana/public/home/sample_data_client.js index 9411373004c25c..eca88604a559da 100644 --- a/src/legacy/core_plugins/kibana/public/home/sample_data_client.js +++ b/src/legacy/core_plugins/kibana/public/home/sample_data_client.js @@ -26,11 +26,11 @@ function clearIndexPatternsCache() { } export async function listSampleDataSets() { - return await getServices().kfetch({ method: 'GET', pathname: sampleDataUrl }); + return await getServices().http.get(sampleDataUrl); } export async function installSampleDataSet(id, sampleDataDefaultIndex) { - await getServices().kfetch({ method: 'POST', pathname: `${sampleDataUrl}/${id}` }); + await getServices().http.post(`${sampleDataUrl}/${id}`); if (getServices().uiSettings.isDefault('defaultIndex')) { getServices().uiSettings.set('defaultIndex', sampleDataDefaultIndex); @@ -40,7 +40,7 @@ export async function installSampleDataSet(id, sampleDataDefaultIndex) { } export async function uninstallSampleDataSet(id, sampleDataDefaultIndex) { - await getServices().kfetch({ method: 'DELETE', pathname: `${sampleDataUrl}/${id}` }); + await getServices().http.delete(`${sampleDataUrl}/${id}`); const uiSettings = getServices().uiSettings; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap index 08a8cf3898e948..8e7ae33a600682 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap @@ -463,7 +463,7 @@ exports[`Field for boolean setting should render as read only if saving is disab display="row" error={null} fullWidth={false} - hasChildLabel={true} + hasChildLabel={false} hasEmptyLabelSpace={false} helpText={null} isInvalid={false} @@ -557,7 +557,7 @@ exports[`Field for boolean setting should render as read only with help text if display="row" error={null} fullWidth={false} - hasChildLabel={true} + hasChildLabel={false} hasEmptyLabelSpace={false} helpText={ diff --git a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/field.js b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/field.js index e52f789eb8af78..a953d09906ed11 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/field.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/field.js @@ -195,7 +195,7 @@ export class Field extends PureComponent { this.setState({ unsavedValue: newUnsavedValue, isInvalid, - error + error, }); }; @@ -764,6 +764,7 @@ export class Field extends PureComponent { helpText={this.renderHelpText(setting)} describedByIds={[`${setting.name}-aria`]} className="mgtAdvancedSettings__fieldRow" + hasChildLabel={setting.type !== 'boolean'} > {this.renderField(setting)} diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index f8cdfa956aa605..c0a7615f207ed5 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -23,43 +23,48 @@ import { i18n } from '@kbn/i18n'; import '../saved_visualizations/saved_visualizations'; import './visualization_editor'; import './visualization'; -import 'ui/vis/editors/default/sidebar'; -import 'ui/visualize'; -import 'ui/collapsible_sidebar'; -import 'ui/directives/storage'; -import { capabilities } from 'ui/capabilities'; -import chrome from 'ui/chrome'; import React from 'react'; -import angular from 'angular'; import { FormattedMessage } from '@kbn/i18n/react'; -import { toastNotifications } from 'ui/notify'; -import { docTitle } from 'ui/doc_title'; -import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; -import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory'; import { migrateAppState } from './lib'; -import uiRoutes from 'ui/routes'; -import { uiModules } from 'ui/modules'; import editorTemplate from './editor.html'; import { DashboardConstants } from '../../dashboard/dashboard_constants'; import { VisualizeConstants } from '../visualize_constants'; -import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; -import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; -import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; -import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; -import { timefilter } from 'ui/timefilter'; -import { showShareContextMenu, ShareContextMenuExtensionsRegistryProvider } from 'ui/share'; -import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing'; -import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; -import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; -import { npStart } from 'ui/new_platform'; import { extractTimeFilter, changeTimeFilter } from '../../../../data/public'; -import { start as data } from '../../../../data/public/legacy'; -import { start as visualizations } from '../../../../visualizations/public/np_ready/public/legacy'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; +import { + getServices, + angular, + absoluteToParsedUrl, + getUnhashableStatesProvider, + KibanaParsedUrl, + migrateLegacyQuery, + SavedObjectSaveModal, + showShareContextMenu, + showSaveModal, + stateMonitorFactory, + subscribeWithScope, +} from '../kibana_services'; + +const { + capabilities, + chrome, + chromeLegacy, + data, + docTitle, + FilterBarQueryFilterProvider, + getBasePath, + ShareContextMenuExtensionsRegistryProvider, + toastNotifications, + timefilter, + uiModules, + uiRoutes, + visualizations, +} = getServices(); + const { savedQueryService } = data.search.services; uiRoutes @@ -101,7 +106,7 @@ uiRoutes savedVis: function (savedVisualizations, redirectWhenMissing, $route) { return savedVisualizations.get($route.current.params.id) .then((savedVis) => { - npStart.core.chrome.recentlyAccessed.add( + chrome.recentlyAccessed.add( savedVis.getFullPath(), savedVis.title, savedVis.id); @@ -169,7 +174,7 @@ function VisEditor( dirty: !savedVis.id }; - $scope.topNavMenu = [...(capabilities.get().visualize.save ? [{ + $scope.topNavMenu = [...(capabilities.visualize.save ? [{ id: 'save', label: i18n.translate('kbn.topNavMenu.saveVisualizationButtonLabel', { defaultMessage: 'save' }), description: i18n.translate('kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel', { @@ -238,7 +243,7 @@ function VisEditor( showShareContextMenu({ anchorElement, allowEmbed: true, - allowShortUrl: capabilities.get().visualize.createShortUrl, + allowShortUrl: capabilities.visualize.createShortUrl, getUnhashableStates, objectId: savedVis.id, objectType: 'visualization', @@ -355,9 +360,9 @@ function VisEditor( } }); - $scope.showSaveQuery = capabilities.get().visualize.saveQuery; + $scope.showSaveQuery = capabilities.visualize.saveQuery; - $scope.$watch(() => capabilities.get().visualize.saveQuery, (newCapability) => { + $scope.$watch(() => capabilities.visualize.saveQuery, (newCapability) => { $scope.showSaveQuery = newCapability; }); @@ -578,7 +583,7 @@ function VisEditor( if ($scope.isAddToDashMode()) { const savedVisualizationParsedUrl = new KibanaParsedUrl({ - basePath: chrome.getBasePath(), + basePath: getBasePath(), appId: kbnBaseUrl.slice('/app/'.length), appPath: kbnUrl.eval(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id }), }); @@ -587,15 +592,15 @@ function VisEditor( // Since we aren't reloading the page, only inserting a new browser history item, we need to manually update // the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved // url, not the unsaved one. - chrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl); + chromeLegacy.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl); - const lastDashboardAbsoluteUrl = npStart.core.chrome.navLinks.get('kibana:dashboard').url; - const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, chrome.getBasePath()); + const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url; + const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, getBasePath()); dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id); kbnUrl.change(dashboardParsedUrl.appPath); } else if (savedVis.id === $route.current.params.id) { docTitle.change(savedVis.lastSavedTitle); - chrome.breadcrumbs.set($injector.invoke(getEditBreadcrumbs)); + chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs)); savedVis.vis.title = savedVis.title; savedVis.vis.description = savedVis.description; // it's needed to save the state to update url string diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js index ade806bc2fc31c..198fbe19a0b7aa 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization.js @@ -17,9 +17,9 @@ * under the License. */ -import { uiModules } from 'ui/modules'; -import 'angular-sanitize'; -import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { getServices } from '../kibana_services'; + +const { embeddables, uiModules } = getServices(); uiModules .get('kibana/directive', ['ngSanitize']) diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js index 2cf6e8e7cf86ea..ead77e6bc41d50 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/visualization_editor.js @@ -17,9 +17,9 @@ * under the License. */ -import { uiModules } from 'ui/modules'; -import 'angular-sanitize'; -import { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types'; +import { getServices, VisEditorTypesRegistryProvider } from '../kibana_services'; + +const { uiModules } = getServices(); uiModules .get('kibana/directive', ['ngSanitize']) diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/disabled_lab_embeddable.tsx b/src/legacy/core_plugins/kibana/public/visualize/embeddable/disabled_lab_embeddable.tsx index 92bd0fa345fa0c..065feae0455972 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/disabled_lab_embeddable.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/disabled_lab_embeddable.tsx @@ -19,7 +19,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Embeddable, EmbeddableOutput } from '../../../../../../plugins/embeddable/public'; + +import { Embeddable, EmbeddableOutput } from '../kibana_services'; import { DisabledLabVisualization } from './disabled_lab_visualization'; import { VisualizeInput } from './visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/get_index_pattern.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/get_index_pattern.ts index 699fa68b4528bd..b6d5adcd98bd02 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/get_index_pattern.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/get_index_pattern.ts @@ -17,9 +17,14 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { StaticIndexPattern, getFromSavedObject } from 'ui/index_patterns'; -import { VisSavedObject } from 'ui/visualize/loader/types'; +import { + getServices, + getFromSavedObject, + StaticIndexPattern, + VisSavedObject, +} from '../kibana_services'; + +const { savedObjectsClient, uiSettings } = getServices(); export async function getIndexPattern( savedVis: VisSavedObject @@ -28,9 +33,7 @@ export async function getIndexPattern( return savedVis.vis.indexPattern; } - const config = chrome.getUiSettingsClient(); - const savedObjectsClient = chrome.getSavedObjectsClient(); - const defaultIndex = config.get('defaultIndex'); + const defaultIndex = uiSettings.get('defaultIndex'); if (savedVis.vis.params.index_pattern) { const indexPatternObjects = await savedObjectsClient.find({ diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index 56aaea1c240f1d..318686b26f6f27 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -18,29 +18,31 @@ */ import _ from 'lodash'; -import { StaticIndexPattern } from 'ui/index_patterns'; -import { PersistedState } from 'ui/persisted_state'; -import { VisualizeLoader } from 'ui/visualize/loader'; import { EmbeddedVisualizeHandler } from 'ui/visualize/loader/embedded_visualize_handler'; -import { AppState } from 'ui/state_management/app_state'; -import { - VisSavedObject, - VisualizeLoaderParams, - VisualizeUpdateParams, -} from 'ui/visualize/loader/types'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; -import { Filter } from '@kbn/es-query'; -import { TimeRange, onlyDisabledFiltersChanged } from '../../../../../../plugins/data/public'; import { - EmbeddableInput, - EmbeddableOutput, - Embeddable, - Container, -} from '../../../../../../plugins/embeddable/public'; + TimeRange, + onlyDisabledFiltersChanged, + esFilters, +} from '../../../../../../plugins/data/public'; import { Query } from '../../../../data/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; +import { + AppState, + Container, + Embeddable, + EmbeddableInput, + EmbeddableOutput, + PersistedState, + StaticIndexPattern, + VisSavedObject, + VisualizeLoader, + VisualizeLoaderParams, + VisualizeUpdateParams, +} from '../kibana_services'; + const getKeys = (o: T): Array => Object.keys(o) as Array; export interface VisualizeEmbeddableConfiguration { @@ -56,7 +58,7 @@ export interface VisualizeEmbeddableConfiguration { export interface VisualizeInput extends EmbeddableInput { timeRange?: TimeRange; query?: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; vis?: { colors?: { [key: string]: string }; }; @@ -80,7 +82,7 @@ export class VisualizeEmbeddable extends Embeddable & { id: string }, parent?: Container ): Promise { - const $injector = await chrome.dangerouslyGetActiveInjector(); + const $injector = await getInjector(); const config = $injector.get('config'); const savedVisualizations = $injector.get('savedVisualizations'); try { const visId = savedObject.id as string; - const editUrl = visId - ? chrome.addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`) - : ''; + const editUrl = visId ? addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`) : ''; const loader = await getVisualizeLoader(); const isLabsEnabled = config.get('visualize:enableLabs'); @@ -179,7 +164,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< input: Partial & { id: string }, parent?: Container ): Promise { - const $injector = await chrome.dangerouslyGetActiveInjector(); + const $injector = await getInjector(); const savedVisualizations = $injector.get('savedVisualizations'); try { @@ -206,8 +191,5 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< } VisualizeEmbeddableFactory.createVisualizeEmbeddableFactory().then(embeddableFactory => { - npSetup.plugins.embeddable.registerEmbeddableFactory( - VISUALIZE_EMBEDDABLE_TYPE, - embeddableFactory - ); + embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js index d95f7ea85c5db2..40a1b79ea35203 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js +++ b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js @@ -20,7 +20,10 @@ import React, { Fragment, PureComponent } from 'react'; import { EuiButton, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; + +import { getServices } from '../kibana_services'; + +const { docLinks } = getServices(); export class HelpMenu extends PureComponent { render() { @@ -31,7 +34,7 @@ export class HelpMenu extends PureComponent { diff --git a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js index aeabff2d97007b..58a92193de63e8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js +++ b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js @@ -22,7 +22,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { HelpMenu } from './help_menu'; export function addHelpMenuToAppChrome(chrome) { - chrome.helpExtension.set(domElement => { + chrome.setHelpExtension(domElement => { render(, domElement); return () => { unmountComponentAtNode(domElement); diff --git a/src/legacy/core_plugins/kibana/public/visualize/index.js b/src/legacy/core_plugins/kibana/public/visualize/index.js index b3c16fb94d7fbc..592a355a71b0d7 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/index.js +++ b/src/legacy/core_plugins/kibana/public/visualize/index.js @@ -21,16 +21,14 @@ import './editor/editor'; import { i18n } from '@kbn/i18n'; import './saved_visualizations/_saved_vis'; import './saved_visualizations/saved_visualizations'; -import uiRoutes from 'ui/routes'; -import 'ui/capabilities/route_setup'; import visualizeListingTemplate from './listing/visualize_listing.html'; import { VisualizeListingController } from './listing/visualize_listing'; import { VisualizeConstants } from './visualize_constants'; -import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; import { getLandingBreadcrumbs, getWizardStep1Breadcrumbs } from './breadcrumbs'; -// load directives -import '../../../data/public'; +import { getServices, FeatureCatalogueCategory } from './kibana_services'; + +const { FeatureCatalogueRegistryProvider, uiRoutes } = getServices(); uiRoutes .defaults(/visualize/, { diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts new file mode 100644 index 00000000000000..7e8435bbdc65ea --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -0,0 +1,132 @@ +/* + * 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 'angular-sanitize'; // used in visualization_editor.js and visualization.js +import 'ui/collapsible_sidebar'; // used in default editor +import 'ui/vis/editors/default/sidebar'; +// load directives +import '../../../data/public'; + +import { npStart } from 'ui/new_platform'; +import angular from 'angular'; // just used in editor.js +import chromeLegacy from 'ui/chrome'; + +import uiRoutes from 'ui/routes'; + +// @ts-ignore +import { docTitle } from 'ui/doc_title'; +import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; +import { wrapInI18nContext } from 'ui/i18n'; +// @ts-ignore +import { uiModules } from 'ui/modules'; +import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue'; +import { ShareContextMenuExtensionsRegistryProvider } from 'ui/share'; +import { timefilter } from 'ui/timefilter'; + +// Saved objects +import { SavedObjectsClientProvider } from 'ui/saved_objects'; +// @ts-ignore +import { SavedObjectProvider } from 'ui/saved_objects/saved_object'; +import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; + +import { createUiStatsReporter, METRIC_TYPE } from '../../../ui_metric/public'; +import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy'; +import { start as data } from '../../../data/public/legacy'; +import { start as embeddables } from '../../../../core_plugins/embeddable_api/public/np_ready/public/legacy'; + +const services = { + // new platform + addBasePath: npStart.core.http.basePath.prepend, + capabilities: npStart.core.application.capabilities, + chrome: npStart.core.chrome, + docLinks: npStart.core.docLinks, + embeddable: npStart.plugins.embeddable, + getBasePath: npStart.core.http.basePath.get, + savedObjectsClient: npStart.core.savedObjects.client, + toastNotifications: npStart.core.notifications.toasts, + uiSettings: npStart.core.uiSettings, + + data, + embeddables, + visualizations, + + // legacy + chromeLegacy, + docTitle, + FeatureCatalogueRegistryProvider, + FilterBarQueryFilterProvider, + getInjector: () => { + return chromeLegacy.dangerouslyGetActiveInjector(); + }, + SavedObjectProvider, + SavedObjectRegistryProvider, + SavedObjectsClientProvider, + ShareContextMenuExtensionsRegistryProvider, + timefilter, + uiModules, + uiRoutes, + wrapInI18nContext, + + createUiStatsReporter, +}; + +export function getServices() { + return services; +} + +// export legacy static dependencies +export { angular }; +export { getFromSavedObject } from 'ui/index_patterns'; +export { PersistedState } from 'ui/persisted_state'; +// @ts-ignore +export { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types'; +// @ts-ignore +export { getUnhashableStatesProvider } from 'ui/state_management/state_hashing'; +export { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; +export { showShareContextMenu } from 'ui/share'; +export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory'; +export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; +export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; +export { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; +export { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; +export { getVisualizeLoader } from 'ui/visualize/loader'; +export { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; +export { + Container, + Embeddable, + EmbeddableFactory, + EmbeddableInput, + EmbeddableOutput, + ErrorEmbeddable, +} from '../../../../../plugins/embeddable/public'; + +// export types +export { METRIC_TYPE }; +export { StaticIndexPattern } from 'ui/index_patterns'; +export { AppState } from 'ui/state_management/app_state'; +export { VisType } from 'ui/vis'; +export { VisualizeLoader } from 'ui/visualize/loader'; +export { + VisSavedObject, + VisualizeLoaderParams, + VisualizeUpdateParams, +} from 'ui/visualize/loader/types'; + +// export const +export { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/listing/no_visualizations_prompt.js b/src/legacy/core_plugins/kibana/public/visualize/listing/no_visualizations_prompt.js index 54a6048a22dafa..34b662838880e3 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/listing/no_visualizations_prompt.js +++ b/src/legacy/core_plugins/kibana/public/visualize/listing/no_visualizations_prompt.js @@ -18,7 +18,8 @@ */ import React from 'react'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { KuiEmptyTablePrompt, @@ -27,7 +28,7 @@ import { KuiButtonIcon, } from '@kbn/ui-framework/components'; -function NoVisualizationsPromptUi({ onCreateVis, intl }) { +function NoVisualizationsPrompt({ onCreateVis }) { return ( } + icon={} > } - message={intl.formatMessage({ - id: 'kbn.visualize.listing.noVisualizationsText', + message={i18n.translate('kbn.visualize.listing.noVisualizationsText', { defaultMessage: `Looks like you don't have any visualizations. Let's create some!`, })} /> @@ -52,4 +52,4 @@ function NoVisualizationsPromptUi({ onCreateVis, intl }) { ); } -export const NoVisualizationsPrompt = injectI18n(NoVisualizationsPromptUi); +export { NoVisualizationsPrompt }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing.js b/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing.js index bb05ce34413db1..f9e3a1a90115a3 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing.js +++ b/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing.js @@ -17,21 +17,27 @@ * under the License. */ -import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; -import 'ui/directives/kbn_href'; -import { uiModules } from 'ui/modules'; -import { timefilter } from 'ui/timefilter'; -import chrome from 'ui/chrome'; -import { wrapInI18nContext } from 'ui/i18n'; -import { toastNotifications } from 'ui/notify'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; -import { SavedObjectsClientProvider } from 'ui/saved_objects'; import { VisualizeListingTable } from './visualize_listing_table'; import { NewVisModal } from '../wizard/new_vis_modal'; import { VisualizeConstants } from '../visualize_constants'; -import { start as visualizations } from '../../../../visualizations/public/np_ready/public/legacy'; import { i18n } from '@kbn/i18n'; +import { getServices } from '../kibana_services'; + +const { + addBasePath, + chrome, + chromeLegacy, + SavedObjectRegistryProvider, + SavedObjectsClientProvider, + timefilter, + toastNotifications, + uiModules, + wrapInI18nContext, + visualizations, +} = getServices(); + const app = uiModules.get('app/visualize', ['ngRoute', 'react']); app.directive('visualizeListingTable', reactDirective => reactDirective(wrapInI18nContext(VisualizeListingTable)) @@ -55,11 +61,11 @@ export function VisualizeListingController($injector, createNewVis) { this.editItem = ({ editUrl }) => { // for visualizations the edit and view URLs are the same - window.location = chrome.addBasePath(editUrl); + window.location.href = addBasePath(editUrl); }; this.getViewUrl = ({ editUrl }) => { - return chrome.addBasePath(editUrl); + return addBasePath(editUrl); }; this.closeNewVisModal = () => { @@ -101,7 +107,7 @@ export function VisualizeListingController($injector, createNewVis) { }) ) .then(() => { - chrome.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id)); + chromeLegacy.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id)); }) .catch(error => { toastNotifications.addError(error, { @@ -112,7 +118,7 @@ export function VisualizeListingController($injector, createNewVis) { }); }; - chrome.breadcrumbs.set([ + chrome.setBreadcrumbs([ { text: i18n.translate('kbn.visualize.visualizeListingBreadcrumbsTitle', { defaultMessage: 'Visualize', diff --git a/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js b/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js index c909b6003516f4..fbd70a0d8c0f77 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js +++ b/src/legacy/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js @@ -19,69 +19,53 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { capabilities } from 'ui/capabilities'; import { TableListView } from './../../table_list_view'; -import { - EuiIcon, - EuiBetaBadge, - EuiLink, - EuiButton, - EuiEmptyPrompt, -} from '@elastic/eui'; +import { EuiIcon, EuiBetaBadge, EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui'; -class VisualizeListingTableUi extends Component { +import { getServices } from '../kibana_services'; +const { capabilities } = getServices(); + +class VisualizeListingTable extends Component { constructor(props) { super(props); } render() { - const { intl } = this.props; return ( item.canDelete} initialFilter={''} noItemsFragment={this.getNoItemsMessage()} - entityName={ - intl.formatMessage({ - id: 'kbn.visualize.listing.table.entityName', - defaultMessage: 'visualization', - }) - } - entityNamePlural={ - intl.formatMessage({ - id: 'kbn.visualize.listing.table.entityNamePlural', - defaultMessage: 'visualizations', - }) - } - tableListTitle={ - intl.formatMessage({ - id: 'kbn.visualize.listing.table.listTitle', - defaultMessage: 'Visualizations', - }) - } + entityName={i18n.translate('kbn.visualize.listing.table.entityName', { + defaultMessage: 'visualization', + })} + entityNamePlural={i18n.translate('kbn.visualize.listing.table.entityNamePlural', { + defaultMessage: 'visualizations', + })} + tableListTitle={i18n.translate('kbn.visualize.listing.table.listTitle', { + defaultMessage: 'Visualizations', + })} /> ); } getTableColumns() { - const { intl } = this.props; const tableColumns = [ { field: 'title', - name: intl.formatMessage({ - id: 'kbn.visualize.listing.table.titleColumnName', + name: i18n.translate('kbn.visualize.listing.table.titleColumnName', { defaultMessage: 'Title', }), sortable: true, @@ -92,35 +76,29 @@ class VisualizeListingTableUi extends Component { > {field} - ) + ), }, { field: 'typeTitle', - name: intl.formatMessage({ - id: 'kbn.visualize.listing.table.typeColumnName', + name: i18n.translate('kbn.visualize.listing.table.typeColumnName', { defaultMessage: 'Type', }), sortable: true, - render: (field, record) => ( + render: (field, record) => ( {this.renderItemTypeIcon(record)} {record.typeTitle} {this.getBadge(record)} - ) + ), }, { field: 'description', - name: intl.formatMessage({ - id: 'kbn.dashboard.listing.table.descriptionColumnName', + name: i18n.translate('kbn.dashboard.listing.table.descriptionColumnName', { defaultMessage: 'Description', }), sortable: true, - render: (field, record) => ( - - {record.description} - - ) + render: (field, record) => {record.description}, }, ]; @@ -184,19 +162,13 @@ class VisualizeListingTableUi extends Component { />
); - } renderItemTypeIcon(item) { let icon; if (item.image) { icon = ( - + ); } else { icon = ( @@ -214,38 +186,40 @@ class VisualizeListingTableUi extends Component { getBadge(item) { if (item.stage === 'beta') { - return (); + return ( + + ); } else if (item.stage === 'experimental') { - return (); + return ( + + ); } } } -VisualizeListingTableUi.propTypes = { +VisualizeListingTable.propTypes = { deleteItems: PropTypes.func.isRequired, findItems: PropTypes.func.isRequired, createItem: PropTypes.func.isRequired, @@ -254,4 +228,4 @@ VisualizeListingTableUi.propTypes = { listingLimit: PropTypes.number.isRequired, }; -export const VisualizeListingTable = injectI18n(VisualizeListingTableUi); +export { VisualizeListingTable }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/types.d.ts b/src/legacy/core_plugins/kibana/public/visualize/types.d.ts index b321e5563eb60b..c83f7f5a5da8b0 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/types.d.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/types.d.ts @@ -17,7 +17,7 @@ * under the License. */ -import { VisSavedObject } from 'ui/visualize/loader/types'; +import { VisSavedObject } from './kibana_services'; export interface SavedVisualizations { urlFor: (id: string) => string; diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx index 6b2f51c3dcf2b1..99d9590e750fd0 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.tsx @@ -20,14 +20,33 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { settingsGet } from './new_vis_modal.test.mocks'; - import { NewVisModal } from './new_vis_modal'; - -import { VisType } from 'ui/vis'; +import { VisType } from '../kibana_services'; import { TypesStart } from '../../../../visualizations/public/np_ready/public/types'; +jest.mock('../kibana_services', () => { + const mock = { + addBasePath: jest.fn(path => `root${path}`), + uiSettings: { get: jest.fn() }, + createUiStatsReporter: () => jest.fn(), + }; + + return { + getServices: () => mock, + VisType: {}, + METRIC_TYPE: 'metricType', + }; +}); + +import { getServices } from '../kibana_services'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + describe('NewVisModal', () => { + const settingsGet = getServices().uiSettings.get as jest.Mock; + const defaultVisTypeParams = { hidden: false, visualization: class Controller { diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx index 31ddafb4ec719a..420f0e5198056c 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.tsx @@ -22,14 +22,15 @@ import React from 'react'; import { EuiModal, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; -import { VisType } from 'ui/vis'; import { VisualizeConstants } from '../visualize_constants'; -import { createUiStatsReporter, METRIC_TYPE } from '../../../../ui_metric/public'; import { SearchSelection } from './search_selection'; import { TypeSelection } from './type_selection'; import { TypesStart, VisTypeAlias } from '../../../../visualizations/public/np_ready/public/types'; +import { getServices, METRIC_TYPE, VisType } from '../kibana_services'; + +const { addBasePath, createUiStatsReporter, uiSettings } = getServices(); + interface TypeSelectionProps { isOpen: boolean; onClose: () => void; @@ -54,7 +55,7 @@ class NewVisModal extends React.Component void; visType: VisType; diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.test.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.test.tsx index 7fd1df85cee067..382f475669f5dc 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.test.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.test.tsx @@ -20,14 +20,21 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { NewVisHelp } from './new_vis_help'; -import chrome from 'ui/chrome'; -jest.doMock('ui/chrome'); +jest.mock('../../kibana_services', () => { + return { + getServices: () => ({ + addBasePath: jest.fn((url: string) => `testbasepath${url}`), + }), + }; +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); describe('NewVisHelp', () => { it('should render as expected', () => { - (chrome.addBasePath as unknown) = (url: string) => `testbasepath${url}`; - expect( shallowWithIntl( { aliasUrl: '/my/fancy/new/thing', description: 'Some desc', highlighted: false, - icon: 'wahtever', + icon: 'whatever', name: 'whatever', promotion: { buttonText: 'Do it now!', diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx b/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx index 3877c01dcc6729..44c36f7d17d550 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx +++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/type_selection/new_vis_help.tsx @@ -20,9 +20,10 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; import { EuiText, EuiButton } from '@elastic/eui'; -import chrome from 'ui/chrome'; import { VisTypeAliasListEntry } from './type_selection'; +import { getServices } from '../../kibana_services'; + interface Props { promotedTypes: VisTypeAliasListEntry[]; } @@ -42,7 +43,7 @@ export function NewVisHelp(props: Props) { {t.promotion!.description}

{ // `config` is used internally and not intended to be set config: Joi.string().default(Joi.ref('$defaultConfigPath')), banner: Joi.boolean().default(true), + lastVersionChecked: Joi.string() + .allow('') + .default(''), url: Joi.when('$dev', { is: true, then: Joi.string().default( @@ -77,7 +80,8 @@ const telemetry = (kibana: any) => { }, }, async replaceInjectedVars(originalInjectedVars: any, request: any) { - const telemetryOptedIn = await getTelemetryOptIn(request); + const currentKibanaVersion = getCurrentKibanaVersion(request.server); + const telemetryOptedIn = await getTelemetryOptIn({ request, currentKibanaVersion }); return { ...originalInjectedVars, @@ -97,7 +101,13 @@ const telemetry = (kibana: any) => { mappings, }, init(server: Server) { - const initializerContext = {} as PluginInitializerContext; + const initializerContext = { + env: { + packageInfo: { + version: getCurrentKibanaVersion(server), + }, + }, + } as PluginInitializerContext; const coreSetup = ({ http: { server }, @@ -116,3 +126,7 @@ const telemetry = (kibana: any) => { // eslint-disable-next-line import/no-default-export export default telemetry; + +function getCurrentKibanaVersion(server: Server): string { + return server.config().get('pkg.version'); +} diff --git a/src/legacy/core_plugins/telemetry/mappings.json b/src/legacy/core_plugins/telemetry/mappings.json index d83f7f59676306..1245ef88f58929 100644 --- a/src/legacy/core_plugins/telemetry/mappings.json +++ b/src/legacy/core_plugins/telemetry/mappings.json @@ -3,6 +3,9 @@ "properties": { "enabled": { "type": "boolean" + }, + "lastVersionChecked": { + "type": "keyword" } } } diff --git a/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.test.ts b/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.test.ts new file mode 100644 index 00000000000000..67ad3aaae427d9 --- /dev/null +++ b/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.test.ts @@ -0,0 +1,214 @@ +/* + * 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 { getTelemetryOptIn } from './get_telemetry_opt_in'; + +describe('get_telemetry_opt_in', () => { + it('returns false when request path is not /app*', async () => { + const params = getCallGetTelemetryOptInParams({ + requestPath: '/foo/bar', + }); + + const result = await callGetTelemetryOptIn(params); + + expect(result).toBe(false); + }); + + it('returns null when saved object not found', async () => { + const params = getCallGetTelemetryOptInParams({ + savedObjectNotFound: true, + }); + + const result = await callGetTelemetryOptIn(params); + + expect(result).toBe(null); + }); + + it('returns false when saved object forbidden', async () => { + const params = getCallGetTelemetryOptInParams({ + savedObjectForbidden: true, + }); + + const result = await callGetTelemetryOptIn(params); + + expect(result).toBe(false); + }); + + it('throws an error on unexpected saved object error', async () => { + const params = getCallGetTelemetryOptInParams({ + savedObjectOtherError: true, + }); + + let threw = false; + try { + await callGetTelemetryOptIn(params); + } catch (err) { + threw = true; + expect(err.message).toBe(SavedObjectOtherErrorMessage); + } + + expect(threw).toBe(true); + }); + + it('returns null if enabled is null or undefined', async () => { + for (const enabled of [null, undefined]) { + const params = getCallGetTelemetryOptInParams({ + enabled, + }); + + const result = await callGetTelemetryOptIn(params); + + expect(result).toBe(null); + } + }); + + it('returns true when enabled is true', async () => { + const params = getCallGetTelemetryOptInParams({ + enabled: true, + }); + + const result = await callGetTelemetryOptIn(params); + + expect(result).toBe(true); + }); + + // build a table of tests with version checks, with results for enabled false + type VersionCheckTable = Array>; + + const EnabledFalseVersionChecks: VersionCheckTable = [ + { lastVersionChecked: '8.0.0', currentKibanaVersion: '8.0.0', result: false }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: '8.0.1', result: false }, + { lastVersionChecked: '8.0.1', currentKibanaVersion: '8.0.0', result: false }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: '8.1.0', result: null }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: '9.0.0', result: null }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: '7.0.0', result: false }, + { lastVersionChecked: '8.1.0', currentKibanaVersion: '8.0.0', result: false }, + { lastVersionChecked: '8.0.0-X', currentKibanaVersion: '8.0.0', result: false }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: '8.0.0-X', result: false }, + { lastVersionChecked: null, currentKibanaVersion: '8.0.0', result: null }, + { lastVersionChecked: undefined, currentKibanaVersion: '8.0.0', result: null }, + { lastVersionChecked: 5, currentKibanaVersion: '8.0.0', result: null }, + { lastVersionChecked: '8.0.0', currentKibanaVersion: 'beta', result: null }, + { lastVersionChecked: 'beta', currentKibanaVersion: '8.0.0', result: null }, + { lastVersionChecked: 'beta', currentKibanaVersion: 'beta', result: false }, + { lastVersionChecked: 'BETA', currentKibanaVersion: 'beta', result: null }, + ].map(el => ({ ...el, enabled: false })); + + // build a table of tests with version checks, with results for enabled true/null/undefined + const EnabledTrueVersionChecks: VersionCheckTable = EnabledFalseVersionChecks.map(el => ({ + ...el, + enabled: true, + result: true, + })); + + const EnabledNullVersionChecks: VersionCheckTable = EnabledFalseVersionChecks.map(el => ({ + ...el, + enabled: null, + result: null, + })); + + const EnabledUndefinedVersionChecks: VersionCheckTable = EnabledFalseVersionChecks.map(el => ({ + ...el, + enabled: undefined, + result: null, + })); + + const AllVersionChecks = [ + ...EnabledFalseVersionChecks, + ...EnabledTrueVersionChecks, + ...EnabledNullVersionChecks, + ...EnabledUndefinedVersionChecks, + ]; + + test.each(AllVersionChecks)( + 'returns expected result for version check with %j', + async (params: Partial) => { + const result = await callGetTelemetryOptIn({ ...DefaultParams, ...params }); + expect(result).toBe(params.result); + } + ); +}); + +interface CallGetTelemetryOptInParams { + requestPath: string; + savedObjectNotFound: boolean; + savedObjectForbidden: boolean; + savedObjectOtherError: boolean; + enabled: boolean | null | undefined; + lastVersionChecked?: any; // should be a string, but test with non-strings + currentKibanaVersion: string; + result?: boolean | null; +} + +const DefaultParams = { + requestPath: '/app/something', + savedObjectNotFound: false, + savedObjectForbidden: false, + savedObjectOtherError: false, + enabled: true, + lastVersionChecked: '8.0.0', + currentKibanaVersion: '8.0.0', +}; + +function getCallGetTelemetryOptInParams( + overrides: Partial +): CallGetTelemetryOptInParams { + return { ...DefaultParams, ...overrides }; +} + +async function callGetTelemetryOptIn(params: CallGetTelemetryOptInParams): Promise { + const { currentKibanaVersion } = params; + const request = getMockRequest(params); + return await getTelemetryOptIn({ request, currentKibanaVersion }); +} + +function getMockRequest(params: CallGetTelemetryOptInParams): any { + return { + path: params.requestPath, + getSavedObjectsClient() { + return getMockSavedObjectsClient(params); + }, + }; +} + +const SavedObjectNotFoundMessage = 'savedObjectNotFound'; +const SavedObjectForbiddenMessage = 'savedObjectForbidden'; +const SavedObjectOtherErrorMessage = 'savedObjectOtherError'; + +function getMockSavedObjectsClient(params: CallGetTelemetryOptInParams) { + return { + async get(type: string, id: string) { + if (params.savedObjectNotFound) throw new Error(SavedObjectNotFoundMessage); + if (params.savedObjectForbidden) throw new Error(SavedObjectForbiddenMessage); + if (params.savedObjectOtherError) throw new Error(SavedObjectOtherErrorMessage); + + const enabled = params.enabled; + const lastVersionChecked = params.lastVersionChecked; + return { attributes: { enabled, lastVersionChecked } }; + }, + errors: { + isNotFoundError(error: any) { + return error.message === SavedObjectNotFoundMessage; + }, + isForbiddenError(error: any) { + return error.message === SavedObjectForbiddenMessage; + }, + }, + }; +} diff --git a/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.ts b/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.ts index 9b365d6dd7ae51..c8bd4a4b6dfbd4 100644 --- a/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.ts +++ b/src/legacy/core_plugins/telemetry/server/get_telemetry_opt_in.ts @@ -17,7 +17,21 @@ * under the License. */ -export async function getTelemetryOptIn(request: any) { +import semver from 'semver'; + +import { SavedObjectAttributes } from './routes/opt_in'; + +interface GetTelemetryOptIn { + request: any; + currentKibanaVersion: string; +} + +// Returns whether telemetry has been opt'ed into or not. +// Returns null not set, meaning Kibana should prompt in the UI. +export async function getTelemetryOptIn({ + request, + currentKibanaVersion, +}: GetTelemetryOptIn): Promise { const isRequestingApplication = request.path.startsWith('/app'); // Prevent interstitial screens (such as the space selector) from prompting for telemetry @@ -27,9 +41,9 @@ export async function getTelemetryOptIn(request: any) { const savedObjectsClient = request.getSavedObjectsClient(); + let savedObject; try { - const { attributes } = await savedObjectsClient.get('telemetry', 'telemetry'); - return attributes.enabled; + savedObject = await savedObjectsClient.get('telemetry', 'telemetry'); } catch (error) { if (savedObjectsClient.errors.isNotFoundError(error)) { return null; @@ -43,4 +57,50 @@ export async function getTelemetryOptIn(request: any) { throw error; } + + const { attributes }: { attributes: SavedObjectAttributes } = savedObject; + + // if enabled is already null, return null + if (attributes.enabled == null) return null; + + const enabled = !!attributes.enabled; + + // if enabled is true, return it + if (enabled === true) return enabled; + + // Additional check if they've already opted out (enabled: false): + // - if the Kibana version has changed by at least a minor version, + // return null to re-prompt. + + const lastKibanaVersion = attributes.lastVersionChecked; + + // if the last kibana version isn't set, or is somehow not a string, return null + if (typeof lastKibanaVersion !== 'string') return null; + + // if version hasn't changed, just return enabled value + if (lastKibanaVersion === currentKibanaVersion) return enabled; + + const lastSemver = parseSemver(lastKibanaVersion); + const currentSemver = parseSemver(currentKibanaVersion); + + // if either version is invalid, return null + if (lastSemver == null || currentSemver == null) return null; + + // actual major/minor version comparison, for cases when to return null + if (currentSemver.major > lastSemver.major) return null; + if (currentSemver.major === lastSemver.major) { + if (currentSemver.minor > lastSemver.minor) return null; + } + + // current version X.Y is not greater than last version X.Y, return enabled + return enabled; +} + +function parseSemver(version: string): semver.SemVer | null { + // semver functions both return nulls AND throw exceptions: "it depends!" + try { + return semver.parse(version); + } catch (err) { + return null; + } } diff --git a/src/legacy/core_plugins/telemetry/server/index.ts b/src/legacy/core_plugins/telemetry/server/index.ts index b8ae5fc231fba6..aa13fab9a5f819 100644 --- a/src/legacy/core_plugins/telemetry/server/index.ts +++ b/src/legacy/core_plugins/telemetry/server/index.ts @@ -25,5 +25,5 @@ export { getTelemetryOptIn } from './get_telemetry_opt_in'; export { telemetryCollectionManager } from './collection_manager'; export const telemetryPlugin = (initializerContext: PluginInitializerContext) => - new TelemetryPlugin(); + new TelemetryPlugin(initializerContext); export { constants }; diff --git a/src/legacy/core_plugins/telemetry/server/plugin.ts b/src/legacy/core_plugins/telemetry/server/plugin.ts index 70de51b2abe995..a5f0f1234799a4 100644 --- a/src/legacy/core_plugins/telemetry/server/plugin.ts +++ b/src/legacy/core_plugins/telemetry/server/plugin.ts @@ -17,14 +17,21 @@ * under the License. */ -import { CoreSetup } from 'src/core/server'; +import { CoreSetup, PluginInitializerContext } from 'src/core/server'; import { registerRoutes } from './routes'; import { telemetryCollectionManager } from './collection_manager'; import { getStats } from './telemetry_collection'; export class TelemetryPlugin { + private readonly currentKibanaVersion: string; + + constructor(initializerContext: PluginInitializerContext) { + this.currentKibanaVersion = initializerContext.env.packageInfo.version; + } + public setup(core: CoreSetup) { + const currentKibanaVersion = this.currentKibanaVersion; telemetryCollectionManager.setStatsGetter(getStats, 'local'); - registerRoutes(core); + registerRoutes({ core, currentKibanaVersion }); } } diff --git a/src/legacy/core_plugins/telemetry/server/routes/index.ts b/src/legacy/core_plugins/telemetry/server/routes/index.ts index 12ba541d699f9f..2eb6bf95b4f45e 100644 --- a/src/legacy/core_plugins/telemetry/server/routes/index.ts +++ b/src/legacy/core_plugins/telemetry/server/routes/index.ts @@ -21,7 +21,12 @@ import { CoreSetup } from 'src/core/server'; import { registerOptInRoutes } from './opt_in'; import { registerTelemetryDataRoutes } from './telemetry_stats'; -export function registerRoutes(core: CoreSetup) { - registerOptInRoutes(core); +interface RegisterRoutesParams { + core: CoreSetup; + currentKibanaVersion: string; +} + +export function registerRoutes({ core, currentKibanaVersion }: RegisterRoutesParams) { + registerOptInRoutes({ core, currentKibanaVersion }); registerTelemetryDataRoutes(core); } diff --git a/src/legacy/core_plugins/telemetry/server/routes/opt_in.ts b/src/legacy/core_plugins/telemetry/server/routes/opt_in.ts index aabc0259f08fc2..3a7194890b5700 100644 --- a/src/legacy/core_plugins/telemetry/server/routes/opt_in.ts +++ b/src/legacy/core_plugins/telemetry/server/routes/opt_in.ts @@ -21,7 +21,17 @@ import Joi from 'joi'; import { boomify } from 'boom'; import { CoreSetup } from 'src/core/server'; -export function registerOptInRoutes(core: CoreSetup) { +interface RegisterOptInRoutesParams { + core: CoreSetup; + currentKibanaVersion: string; +} + +export interface SavedObjectAttributes { + enabled?: boolean; + lastVersionChecked: string; +} + +export function registerOptInRoutes({ core, currentKibanaVersion }: RegisterOptInRoutesParams) { const { server } = core.http as any; server.route({ @@ -36,17 +46,16 @@ export function registerOptInRoutes(core: CoreSetup) { }, handler: async (req: any, h: any) => { const savedObjectsClient = req.getSavedObjectsClient(); + const savedObject: SavedObjectAttributes = { + enabled: req.payload.enabled, + lastVersionChecked: currentKibanaVersion, + }; + const options = { + id: 'telemetry', + overwrite: true, + }; try { - await savedObjectsClient.create( - 'telemetry', - { - enabled: req.payload.enabled, - }, - { - id: 'telemetry', - overwrite: true, - } - ); + await savedObjectsClient.create('telemetry', savedObject, options); } catch (err) { return boomify(err); } diff --git a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts index eb4fb3f397149f..156c06a6055287 100644 --- a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts +++ b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts @@ -18,12 +18,12 @@ */ // @ts-ignore -import { buildEsQuery, getEsQueryConfig, Filter } from '@kbn/es-query'; +import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; // @ts-ignore import { timezoneProvider } from 'ui/vis/lib/timezone'; import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public'; import { Query } from 'src/legacy/core_plugins/data/public'; -import { TimeRange } from 'src/plugins/data/public'; +import { TimeRange, esFilters } from 'src/plugins/data/public'; import { VisParams } from 'ui/vis'; import { i18n } from '@kbn/i18n'; import { TimelionVisualizationDependencies } from '../plugin'; @@ -60,7 +60,7 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep visParams, }: { timeRange: TimeRange; - filters: Filter[]; + filters: esFilters.Filter[]; query: Query; visParams: VisParams; forceFetch?: boolean; diff --git a/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts b/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts index 63adccb3e02b04..7310ee5b5f1720 100644 --- a/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts +++ b/src/legacy/core_plugins/ui_metric/public/services/telemetry_analytics.ts @@ -47,14 +47,20 @@ interface AnalyicsReporterConfig { } export function createAnalyticsReporter(config: AnalyicsReporterConfig) { - const { localStorage, basePath, $http, debug } = config; + const { localStorage, basePath, debug } = config; return createReporter({ debug, storage: localStorage, async http(report) { const url = `${basePath}/api/telemetry/report`; - await $http.post(url, { report }); + await fetch(url, { + method: 'POST', + headers: { + 'kbn-xsrf': 'true', + }, + body: JSON.stringify({ report }), + }); }, }); } diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/get_index_pattern_service.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/get_index_pattern_service.js index 0766e6a48765f9..54e90ab7dd9b7f 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/get_index_pattern_service.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/get_index_pattern_service.js @@ -17,7 +17,7 @@ * under the License. */ -import { IndexPatternsService } from '../../../../server/index_patterns/service'; +import { IndexPatternsFetcher } from '../../../../../plugins/data/server/'; export const getIndexPatternService = { assign: 'indexPatternsService', method(req) { @@ -25,6 +25,6 @@ export const getIndexPatternService = { const callDataCluster = (...args) => { return dataCluster.callWithRequest(req, ...args); }; - return new IndexPatternsService(callDataCluster); + return new IndexPatternsFetcher(callDataCluster); }, }; diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts index 22a71bd999d54e..b4c32f37eb90c1 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts @@ -16,13 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter } from '@kbn/es-query'; + import { timefilter } from 'ui/timefilter'; import { TimeRange } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; - -// @ts-ignore import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; + +import { esFilters } from '../../../../plugins/data/public'; + // @ts-ignore import { VegaParser } from './data_model/vega_parser'; // @ts-ignore @@ -35,7 +36,7 @@ import { VisParams } from './vega_fn'; interface VegaRequestHandlerParams { query: Query; - filters: Filter; + filters: esFilters.Filter; timeRange: TimeRange; visParams: VisParams; } diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js index ec898080c581e8..5abb99e9bf716a 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -27,7 +27,7 @@ import { Utils } from '../data_model/utils'; import { VISUALIZATION_COLORS } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TooltipHandler } from './vega_tooltip'; -import { buildQueryFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../plugins/data/public'; import { getEnableExternalUrls } from '../helpers/vega_config_provider'; @@ -263,7 +263,7 @@ export class VegaBaseView { */ async addFilterHandler(query, index) { const indexId = await this._findIndex(index); - const filter = buildQueryFilter(query, indexId); + const filter = esFilters.buildQueryFilter(query, indexId); this._queryfilter.addFilters(filter); } @@ -274,7 +274,7 @@ export class VegaBaseView { async removeFilterHandler(query, index) { const $injector = await chrome.dangerouslyGetActiveInjector(); const indexId = await this._findIndex(index); - const filter = buildQueryFilter(query, indexId); + const filter = esFilters.buildQueryFilter(query, indexId); // This is a workaround for the https://github.com/elastic/kibana/issues/18863 // Once fixed, replace with a direct call (no await is needed because its not async) diff --git a/src/legacy/server/index_patterns/index.ts b/src/legacy/server/index_patterns/index.ts index eea8a4a998e2dc..75d0038cf9023e 100644 --- a/src/legacy/server/index_patterns/index.ts +++ b/src/legacy/server/index_patterns/index.ts @@ -17,7 +17,9 @@ * under the License. */ -// @ts-ignore no types export { indexPatternsMixin } from './mixin'; -export { IndexPatternsService, FieldDescriptor } from './service'; +export { + IndexPatternsFetcher, + FieldDescriptor, +} from '../../../plugins/data/server/index_patterns/fetcher'; export { IndexPatternsServiceFactory } from './mixin'; diff --git a/src/legacy/server/index_patterns/mixin.ts b/src/legacy/server/index_patterns/mixin.ts index a7180d6a2d70ec..6b04c3842007bd 100644 --- a/src/legacy/server/index_patterns/mixin.ts +++ b/src/legacy/server/index_patterns/mixin.ts @@ -17,11 +17,10 @@ * under the License. */ -import { IndexPatternsService } from './service'; +import { IndexPatternsFetcher } from '../../../plugins/data/server'; import KbnServer from '../kbn_server'; import { APICaller, CallAPIOptions } from '../../../core/server'; import { Legacy } from '../../../../kibana'; -import { registerRoutes } from './routes'; export function indexPatternsMixin(kbnServer: KbnServer, server: Legacy.Server) { /** @@ -31,7 +30,7 @@ export function indexPatternsMixin(kbnServer: KbnServer, server: Legacy.Server) * @type {IndexPatternsService} */ server.decorate('server', 'indexPatternsServiceFactory', ({ callCluster }) => { - return new IndexPatternsService(callCluster); + return new IndexPatternsFetcher(callCluster); }); /** @@ -50,10 +49,8 @@ export function indexPatternsMixin(kbnServer: KbnServer, server: Legacy.Server) ) => callWithRequest(request, endpoint, params, options); return server.indexPatternsServiceFactory({ callCluster }); }); - - registerRoutes(kbnServer.newPlatform.setup.core); } export type IndexPatternsServiceFactory = (args: { callCluster: (endpoint: string, clientParams: any, options: any) => Promise; -}) => IndexPatternsService; +}) => IndexPatternsFetcher; diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index e7f2f4c85435ff..9cc4e30d4252dd 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -150,5 +150,5 @@ export default class KbnServer { export { Server, Request, ResponseToolkit } from 'hapi'; // Re-export commonly accessed api types. -export { IndexPatternsService } from './index_patterns'; +export { IndexPatternsFetcher as IndexPatternsService } from './index_patterns'; export { SavedObjectsLegacyService, SavedObjectsClient } from 'src/core/server'; diff --git a/src/legacy/server/usage/README.md b/src/legacy/server/usage/README.md index ad1bb822b70cda..5c4bcc05bbc383 100644 --- a/src/legacy/server/usage/README.md +++ b/src/legacy/server/usage/README.md @@ -90,5 +90,3 @@ There are a few ways you can test that your usage collector is working properly. Yes. When you talk to the Platform team about new fields being added, point out specifically which properties will have dynamic inner fields. 5. **If I accumulate an event counter in server memory, which my fetch method returns, won't it reset when the Kibana server restarts?** Yes, but that is not a major concern. A visualization on such info might be a date histogram that gets events-per-second or something, which would be impacted by server restarts, so we'll have to offset the beginning of the time range when we detect that the latest metric is smaller than the earliest metric. That would be a pretty custom visualization, but perhaps future Kibana enhancements will be able to support that. -6. **Who can I talk to with more questions?** - The Kibana Platform team is the owner of the telemetry service. You can bring questions to them. You can also talk to Tim Sullivan, who created the Kibana telemetry service, or Chris Earle, who set up the telemetry cluster and AWS Lambas for the upstream prod and staging endpoints that recieve the data sent from end-user browsers. diff --git a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js b/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js index 5dfa2e3d6ae3f6..70bca2e40ae3fa 100644 --- a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js @@ -18,8 +18,9 @@ */ import _ from 'lodash'; -import { buildExistsFilter, buildPhrasesFilter, buildQueryFromFilters } from '@kbn/es-query'; +import { buildQueryFromFilters } from '@kbn/es-query'; import { AggGroupNames } from '../../vis/editors/default/agg_groups'; +import { esFilters } from '../../../../../plugins/data/public'; /** * walks the aggregation DSL and returns DSL starting at aggregation with id of startFromAggId @@ -180,7 +181,7 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) => agg.buckets.some(bucket => bucket.key === '__missing__') ) { filters.push( - buildExistsFilter( + esFilters.buildExistsFilter( aggWithOtherBucket.params.field, aggWithOtherBucket.params.field.indexPattern ) @@ -232,7 +233,7 @@ export const mergeOtherBucketAggResponse = ( ); const requestFilterTerms = getOtherAggTerms(requestAgg, key, otherAgg); - const phraseFilter = buildPhrasesFilter( + const phraseFilter = esFilters.buildPhrasesFilter( otherAgg.params.field, requestFilterTerms, otherAgg.params.field.indexPattern @@ -243,7 +244,7 @@ export const mergeOtherBucketAggResponse = ( if (aggResultBuckets.some(bucket => bucket.key === '__missing__')) { bucket.filters.push( - buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern) + esFilters.buildExistsFilter(otherAgg.params.field, otherAgg.params.field.indexPattern) ); } aggResultBuckets.push(bucket); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts index f67fa55b278598..9426df7d34c29e 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts @@ -18,19 +18,19 @@ */ import moment from 'moment'; -import { RangeFilter } from '@kbn/es-query'; import { createFilterDateHistogram } from './date_histogram'; import { intervalOptions } from '../_interval_options'; import { AggConfigs } from '../../agg_configs'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; import { BUCKET_TYPES } from '../bucket_agg_types'; +import { esFilters } from '../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); describe('AggConfig Filters', () => { describe('date_histogram', () => { let agg: IBucketDateHistogramAggConfig; - let filter: RangeFilter; + let filter: esFilters.RangeFilter; let bucketStart: any; let field: any; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts index 8c6140cc4b37ad..f91a92eab1c33f 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts @@ -18,8 +18,8 @@ */ import moment from 'moment'; -import { buildRangeFilter } from '@kbn/es-query'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterDateHistogram = ( agg: IBucketDateHistogramAggConfig, @@ -28,7 +28,7 @@ export const createFilterDateHistogram = ( const start = moment(key); const interval = agg.buckets.getInterval(); - return buildRangeFilter( + return esFilters.buildRangeFilter( agg.params.field, { gte: start.toISOString(), diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts index cd4b0ffc215b07..01689d954a0723 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts @@ -17,16 +17,16 @@ * under the License. */ -import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import moment from 'moment'; import { IBucketAggConfig } from '../_bucket_agg_type'; import { DateRangeKey } from '../date_range'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterDateRange = (agg: IBucketAggConfig, { from, to }: DateRangeKey) => { - const filter: RangeFilterParams = {}; + const filter: esFilters.RangeFilterParams = {}; if (from) filter.gte = moment(from).toISOString(); if (to) filter.lt = moment(to).toISOString(); if (to && from) filter.format = 'strict_date_optional_time'; - return buildRangeFilter(agg.params.field, filter, agg.getIndexPattern()); + return esFilters.buildRangeFilter(agg.params.field, filter, agg.getIndexPattern()); }; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts index bf30b333056bcf..6b614514580b6d 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts @@ -18,8 +18,8 @@ */ import { get } from 'lodash'; -import { buildQueryFilter } from '@kbn/es-query'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => { // have the aggConfig write agg dsl params @@ -28,6 +28,6 @@ export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => const indexPattern = aggConfig.getIndexPattern(); if (filter && indexPattern && indexPattern.id) { - return buildQueryFilter(filter.query, indexPattern.id, key); + return esFilters.buildQueryFilter(filter.query, indexPattern.id, key); } }; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts index 37d5c6bc8adc1f..fc587fa9ecdb6b 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts @@ -17,14 +17,14 @@ * under the License. */ -import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterHistogram = (aggConfig: IBucketAggConfig, key: string) => { const value = parseInt(key, 10); - const params: RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval }; + const params: esFilters.RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval }; - return buildRangeFilter( + return esFilters.buildRangeFilter( aggConfig.params.field, params, aggConfig.getIndexPattern(), diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts index 83769578725f29..803f6d97ae42d3 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts @@ -17,13 +17,13 @@ * under the License. */ -import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import { CidrMask } from '../../../utils/cidr_mask'; import { IBucketAggConfig } from '../_bucket_agg_type'; import { IpRangeKey } from '../ip_range'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey) => { - let range: RangeFilterParams; + let range: esFilters.RangeFilterParams; if (key.type === 'mask') { range = new CidrMask(key.mask).getRange(); @@ -34,7 +34,7 @@ export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey }; } - return buildRangeFilter( + return esFilters.buildRangeFilter( aggConfig.params.field, { gte: range.from, lte: range.to }, aggConfig.getIndexPattern() diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts index cf2c83884651a1..929827c6e3fec6 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts @@ -17,11 +17,11 @@ * under the License. */ -import { buildRangeFilter } from '@kbn/es-query'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterRange = (aggConfig: IBucketAggConfig, params: any) => { - return buildRangeFilter( + return esFilters.buildRangeFilter( aggConfig.params.field, params, aggConfig.getIndexPattern(), diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts index b00e939eac8d86..42f8349d5a2a3d 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { ExistsFilter, Filter } from '@kbn/es-query'; + import { createFilterTerms } from './terms'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; +import { esFilters } from '../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); @@ -48,7 +49,7 @@ describe('AggConfig Filters', () => { { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); - const filter = createFilterTerms(aggConfigs.aggs[0], 'apache', {}) as Filter; + const filter = createFilterTerms(aggConfigs.aggs[0], 'apache', {}) as esFilters.Filter; expect(filter).toHaveProperty('query'); expect(filter.query).toHaveProperty('match_phrase'); @@ -63,14 +64,14 @@ describe('AggConfig Filters', () => { { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); - const filterFalse = createFilterTerms(aggConfigs.aggs[0], '', {}) as Filter; + const filterFalse = createFilterTerms(aggConfigs.aggs[0], '', {}) as esFilters.Filter; expect(filterFalse).toHaveProperty('query'); expect(filterFalse.query).toHaveProperty('match_phrase'); expect(filterFalse.query.match_phrase).toHaveProperty('field'); expect(filterFalse.query.match_phrase.field).toBeFalsy(); - const filterTrue = createFilterTerms(aggConfigs.aggs[0], '1', {}) as Filter; + const filterTrue = createFilterTerms(aggConfigs.aggs[0], '1', {}) as esFilters.Filter; expect(filterTrue).toHaveProperty('query'); expect(filterTrue.query).toHaveProperty('match_phrase'); @@ -82,7 +83,11 @@ describe('AggConfig Filters', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); - const filter = createFilterTerms(aggConfigs.aggs[0], '__missing__', {}) as ExistsFilter; + const filter = createFilterTerms( + aggConfigs.aggs[0], + '__missing__', + {} + ) as esFilters.ExistsFilter; expect(filter).toHaveProperty('exists'); expect(filter.exists).toHaveProperty('field', 'field'); @@ -98,7 +103,7 @@ describe('AggConfig Filters', () => { const [filter] = createFilterTerms(aggConfigs.aggs[0], '__other__', { terms: ['apache'], - }) as Filter[]; + }) as esFilters.Filter[]; expect(filter).toHaveProperty('query'); expect(filter.query).toHaveProperty('bool'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts index e5d4406c752c7c..5bd770e672786d 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts @@ -17,8 +17,8 @@ * under the License. */ -import { Filter, buildPhraseFilter, buildPhrasesFilter, buildExistsFilter } from '@kbn/es-query'; import { IBucketAggConfig } from '../_bucket_agg_type'; +import { esFilters } from '../../../../../../plugins/data/public'; export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, params: any) => { const field = aggConfig.params.field; @@ -27,20 +27,20 @@ export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, para if (key === '__other__') { const terms = params.terms; - const phraseFilter = buildPhrasesFilter(field, terms, indexPattern); + const phraseFilter = esFilters.buildPhrasesFilter(field, terms, indexPattern); phraseFilter.meta.negate = true; - const filters: Filter[] = [phraseFilter]; + const filters: esFilters.Filter[] = [phraseFilter]; if (terms.some((term: string) => term === '__missing__')) { - filters.push(buildExistsFilter(field, indexPattern)); + filters.push(esFilters.buildExistsFilter(field, indexPattern)); } return filters; } else if (key === '__missing__') { - const existsFilter = buildExistsFilter(field, indexPattern); + const existsFilter = esFilters.buildExistsFilter(field, indexPattern); existsFilter.meta.negate = true; return existsFilter; } - return buildPhraseFilter(field, key, indexPattern); + return esFilters.buildPhraseFilter(field, key, indexPattern); }; diff --git a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts b/src/legacy/ui/public/agg_types/buckets/date_histogram.ts index 4c2e67f758a7e2..e86d561a1c79bd 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/date_histogram.ts @@ -32,7 +32,6 @@ import { ScaleMetricsParamEditor } from '../../vis/editors/default/controls/scal import { dateHistogramInterval } from '../../../../core_plugins/data/public'; import { writeParams } from '../agg_params'; import { AggConfigs } from '../agg_configs'; -import { AggConfig } from '../agg_config'; import { isMetricAggType } from '../metrics/metric_agg_type'; import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; @@ -189,7 +188,7 @@ export const dateHistogramBucketAgg = new BucketAggType isMetricAggType(a.type)); - const all = _.every(metrics, (a: AggConfig) => { + const all = _.every(metrics, (a: IBucketAggConfig) => { const { type } = a; if (isMetricAggType(type)) { diff --git a/src/legacy/ui/public/agg_types/buckets/date_range.ts b/src/legacy/ui/public/agg_types/buckets/date_range.ts index dd7f0cb972ae22..4de6002e2e3746 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/date_range.ts @@ -21,9 +21,8 @@ import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; -import { AggConfig } from '../agg_config'; import { FieldFormat } from '../../../../../plugins/data/common/field_formats'; import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges'; @@ -64,7 +63,7 @@ export const dateRangeBucketAgg = new BucketAggType({ name: 'field', type: 'field', filterFieldTypes: KBN_FIELD_TYPES.DATE, - default(agg: AggConfig) { + default(agg: IBucketAggConfig) { return agg.getIndexPattern().timeFieldName; }, }, @@ -83,7 +82,7 @@ export const dateRangeBucketAgg = new BucketAggType({ default: undefined, // Implimentation method is the same as that of date_histogram serialize: () => undefined, - write: (agg: AggConfig, output: Record) => { + write: (agg: IBucketAggConfig, output: Record) => { const field = agg.getParam('field'); let tz = agg.getParam('time_zone'); diff --git a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts b/src/legacy/ui/public/agg_types/buckets/geo_hash.ts index 555aa94b636b84..1716891231b838 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts +++ b/src/legacy/ui/public/agg_types/buckets/geo_hash.ts @@ -26,7 +26,6 @@ import { IsFilteredByCollarParamEditor } from '../../vis/editors/default/control import { PrecisionParamEditor } from '../../vis/editors/default/controls/precision'; import { geohashColumns } from '../../utils/decode_geo_hash'; import { AggGroupNames } from '../../vis/editors/default/agg_groups'; -import { AggConfig } from '../agg_config'; import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; // @ts-ignore @@ -143,7 +142,7 @@ export const geoHashBucketAgg = new BucketAggType({ }, ], getRequestAggs(agg) { - const aggs: AggConfig[] = []; + const aggs: IBucketAggConfig[] = []; const params = agg.params; if (params.isFilteredByCollar && agg.getField()) { diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.ts b/src/legacy/ui/public/agg_types/buckets/histogram.ts index 74a2da4a0eb674..fba2f47010c34e 100644 --- a/src/legacy/ui/public/agg_types/buckets/histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/histogram.ts @@ -28,7 +28,6 @@ import { NumberIntervalParamEditor } from '../../vis/editors/default/controls/nu import { MinDocCountParamEditor } from '../../vis/editors/default/controls/min_doc_count'; import { HasExtendedBoundsParamEditor } from '../../vis/editors/default/controls/has_extended_bounds'; import { ExtendedBoundsParamEditor } from '../../vis/editors/default/controls/extended_bounds'; -import { AggConfig } from '../agg_config'; import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; import { BUCKET_TYPES } from './bucket_agg_types'; @@ -177,7 +176,7 @@ export const histogramBucketAgg = new BucketAggType({ name: 'min_doc_count', default: false, editorComponent: MinDocCountParamEditor, - write(aggConfig: AggConfig, output: Record) { + write(aggConfig: IBucketAggConfig, output: Record) { if (aggConfig.params.min_doc_count) { output.params.min_doc_count = 0; } else { @@ -198,14 +197,14 @@ export const histogramBucketAgg = new BucketAggType({ max: '', }, editorComponent: ExtendedBoundsParamEditor, - write(aggConfig: AggConfig, output: Record) { + write(aggConfig: IBucketAggConfig, output: Record) { const { min, max } = aggConfig.params.extended_bounds; if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) { output.params.extended_bounds = { min, max }; } }, - shouldShow: (aggConfig: AggConfig) => aggConfig.params.has_extended_bounds, + shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds, }, ], }); diff --git a/src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts b/src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts index 2bf0930d376845..e4527ff87f48cc 100644 --- a/src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts +++ b/src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts @@ -18,7 +18,6 @@ */ import { isString, isObject } from 'lodash'; -import { AggConfig } from 'ui/agg_types'; import { IBucketAggConfig, BucketAggType, BucketAggParam } from './_bucket_agg_type'; export const isType = (type: string) => { @@ -32,12 +31,16 @@ export const isType = (type: string) => { export const isStringType = isType('string'); export const migrateIncludeExcludeFormat = { - serialize(this: BucketAggParam, value: any, agg: AggConfig) { + serialize(this: BucketAggParam, value: any, agg: IBucketAggConfig) { if (this.shouldShow && !this.shouldShow(agg)) return; if (!value || isString(value)) return value; else return value.pattern; }, - write(this: BucketAggType, aggConfig: AggConfig, output: Record) { + write( + this: BucketAggType, + aggConfig: IBucketAggConfig, + output: Record + ) { const value = aggConfig.getParam(this.name); if (isObject(value)) { diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.html b/src/legacy/ui/public/chrome/directives/kbn_chrome.html index 541082e68de58d..7738093fe9dea7 100644 --- a/src/legacy/ui/public/chrome/directives/kbn_chrome.html +++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.html @@ -1,4 +1,4 @@ -
+
( }); $rootScope.$on('$routeChangeSuccess', () => { + if (isDummyWrapperRoute($route)) { + return; + } const current = $route.current || {}; if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { diff --git a/src/legacy/ui/public/styles/_legacy/_base.scss b/src/legacy/ui/public/styles/_legacy/_base.scss index aad90a99ac3576..0fcfb515c7c90b 100644 --- a/src/legacy/ui/public/styles/_legacy/_base.scss +++ b/src/legacy/ui/public/styles/_legacy/_base.scss @@ -4,13 +4,14 @@ input.ng-invalid, textarea.ng-invalid, select.ng-invalid { - &.ng-dirty, &.ng-touched { + &.ng-dirty, + &.ng-touched { border-color: $euiColorDanger !important; } } -input[type="radio"], -input[type="checkbox"], +input[type='radio'], +input[type='checkbox'], .radio, .radio-inline, .checkbox, @@ -18,7 +19,7 @@ input[type="checkbox"], &[disabled], fieldset[disabled] & { cursor: default; - opacity: .8; + opacity: 0.8; } } @@ -27,7 +28,7 @@ input[type="checkbox"], align-items: center; padding-left: 0 !important; - input[type="checkbox"] { + input[type='checkbox'] { float: none; margin: 0 $euiSizeXS; position: static; @@ -95,7 +96,6 @@ input[type="checkbox"], } } - // Too overused in many places to be moved elsewhere .page-row { @@ -114,7 +114,7 @@ input[type="checkbox"], // state. This is useful when you've already hand crafted your own // focus states in Kibana. :focus { - &:not([class^="eui"]):not(.kbn-resetFocusState) { + &:not([class^='eui']):not(.kbn-resetFocusState) { @include euiFocusRing; } } @@ -122,7 +122,8 @@ input[type="checkbox"], // A neccessary hack so that the above focus policy doesn't polute some EUI // entrenched inputs. .euiComboBox { - input:focus { + // :not() specificity needed to override the above + input:not([class^='eui']):focus { animation: none !important; } } diff --git a/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx b/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx index 6d3b5fc4e63557..528914f4fd006b 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx +++ b/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx @@ -26,7 +26,9 @@ import { EuiDraggable, EuiSpacer, EuiPanel, + EuiFormErrorText, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { AggConfig } from '../../../../agg_types/agg_config'; import { aggGroupNamesMap, AggGroupNames } from '../agg_groups'; @@ -80,7 +82,15 @@ function DefaultEditorAggGroup({ const [aggsState, setAggsState] = useReducer(aggGroupReducer, group, initAggsState); - const isGroupValid = Object.values(aggsState).every(item => item.valid); + const bucketsError = + lastParentPipelineAggTitle && groupName === AggGroupNames.Buckets && !group.length + ? i18n.translate('common.ui.aggTypes.buckets.mustHaveBucketErrorMessage', { + defaultMessage: 'Add a bucket with "Date Histogram" or "Histogram" aggregation.', + description: 'Date Histogram and Histogram should not be translated', + }) + : undefined; + + const isGroupValid = !bucketsError && Object.values(aggsState).every(item => item.valid); const isAllAggsTouched = isInvalidAggsTouched(aggsState); const isMetricAggregationDisabled = useMemo( () => groupName === AggGroupNames.Metrics && getEnabledMetricAggsCount(group) === 1, @@ -144,6 +154,12 @@ function DefaultEditorAggGroup({

{groupNameLabel}

+ {bucketsError && ( + <> + {bucketsError} + + + )} <> {group.map((agg: AggConfig, index: number) => ( diff --git a/src/legacy/ui/public/vis/vis_filters/brush_event.js b/src/legacy/ui/public/vis/vis_filters/brush_event.js index 90cbaf7c048ee2..17ab302a20cb16 100644 --- a/src/legacy/ui/public/vis/vis_filters/brush_event.js +++ b/src/legacy/ui/public/vis/vis_filters/brush_event.js @@ -19,7 +19,7 @@ import _ from 'lodash'; import moment from 'moment'; -import { buildRangeFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../plugins/data/public'; export function onBrushEvent(event) { const isNumber = event.data.ordered; @@ -56,7 +56,7 @@ export function onBrushEvent(event) { }; } - const newFilter = buildRangeFilter( + const newFilter = esFilters.buildRangeFilter( field, range, indexPattern, diff --git a/src/legacy/ui/public/vis/vis_filters/vis_filters.js b/src/legacy/ui/public/vis/vis_filters/vis_filters.js index 9343585fa9508c..e879d040125f11 100644 --- a/src/legacy/ui/public/vis/vis_filters/vis_filters.js +++ b/src/legacy/ui/public/vis/vis_filters/vis_filters.js @@ -20,8 +20,8 @@ import _ from 'lodash'; import { pushFilterBarFilters } from '../push_filters'; import { onBrushEvent } from './brush_event'; -import { uniqFilters } from '../../../../../plugins/data/public'; -import { toggleFilterNegated } from '@kbn/es-query'; +import { uniqFilters, esFilters } from '../../../../../plugins/data/public'; + /** * For terms aggregations on `__other__` buckets, this assembles a list of applicable filter * terms based on a specific cell in the tabified data. @@ -94,7 +94,7 @@ const createFiltersFromEvent = (event) => { if (filter) { filter.forEach(f => { if (event.negate) { - f = toggleFilterNegated(f); + f = esFilters.toggleFilterNegated(f); } filters.push(f); }); diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts index bb9f5832ac4e5f..fb16e095b34187 100644 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.ts @@ -22,7 +22,6 @@ import { debounce, forEach, get, isEqual } from 'lodash'; import * as Rx from 'rxjs'; import { share } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; -import { Filter } from '@kbn/es-query'; import { toastNotifications } from 'ui/notify'; // @ts-ignore untyped dependency import { AggConfigs } from 'ui/agg_types/agg_configs'; @@ -44,6 +43,7 @@ import { VisFiltersProvider } from '../../vis/vis_filters'; import { PipelineDataLoader } from './pipeline_data_loader'; import { visualizationLoader } from './visualization_loader'; import { Query } from '../../../../core_plugins/data/public'; +import { esFilters } from '../../../../../plugins/data/public'; import { DataAdapter, RequestAdapter } from '../../inspector/adapters'; @@ -67,7 +67,7 @@ export interface RequestHandlerParams { aggs: AggConfigs; timeRange?: TimeRange; query?: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; forceFetch: boolean; queryFilter: QueryFilter; uiState?: PersistedState; diff --git a/src/legacy/ui/public/visualize/loader/types.ts b/src/legacy/ui/public/visualize/loader/types.ts index 87183d839e6373..525ec35834ecde 100644 --- a/src/legacy/ui/public/visualize/loader/types.ts +++ b/src/legacy/ui/public/visualize/loader/types.ts @@ -17,7 +17,6 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; import { SavedObject } from 'ui/saved_objects/saved_object'; @@ -26,6 +25,7 @@ import { SearchSource } from '../../courier'; import { PersistedState } from '../../persisted_state'; import { AppState } from '../../state_management/app_state'; import { Vis } from '../../vis'; +import { esFilters } from '../../../../../plugins/data/public'; export interface VisSavedObject extends SavedObject { vis: Vis; @@ -68,7 +68,7 @@ export interface VisualizeLoaderParams { /** * Specifies the filters that should be applied to that visualization. */ - filters?: Filter[]; + filters?: esFilters.Filter[]; /** * The query that should apply to that visualization. */ diff --git a/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts b/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts index ec612f7dd03735..9f3aa190917d73 100644 --- a/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts +++ b/src/legacy/ui/public/visualize/loader/utils/query_geohash_bounds.ts @@ -22,13 +22,13 @@ import { get } from 'lodash'; import { toastNotifications } from 'ui/notify'; import { AggConfig } from 'ui/vis'; -import { Filter } from '@kbn/es-query'; import { Query } from 'src/legacy/core_plugins/data/public'; import { timefilter } from 'ui/timefilter'; import { Vis } from '../../../vis'; +import { esFilters } from '../../../../../../plugins/data/public'; interface QueryGeohashBoundsParams { - filters?: Filter[]; + filters?: esFilters.Filter[]; query?: Query; } @@ -76,7 +76,7 @@ export async function queryGeohashBounds(vis: Vis, params: QueryGeohashBoundsPar const useTimeFilter = !!indexPattern.timeFieldName; if (useTimeFilter) { const filter = timefilter.createFilter(indexPattern); - if (filter) activeFilters.push((filter as any) as Filter); + if (filter) activeFilters.push((filter as any) as esFilters.Filter); } return activeFilters; }); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json b/src/plugins/dashboard_embeddable_container/kibana.json similarity index 90% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json rename to src/plugins/dashboard_embeddable_container/kibana.json index 417168ba61dd94..aab23316f606c0 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json +++ b/src/plugins/dashboard_embeddable_container/kibana.json @@ -4,7 +4,7 @@ "requiredPlugins": [ "embeddable", "inspector", - "ui_actions" + "uiActions" ], "server": false, "ui": true diff --git a/src/plugins/dashboard_embeddable_container/public/_index.scss b/src/plugins/dashboard_embeddable_container/public/_index.scss new file mode 100644 index 00000000000000..ccb8299072fb76 --- /dev/null +++ b/src/plugins/dashboard_embeddable_container/public/_index.scss @@ -0,0 +1,5 @@ +@import '../../embeddable/public/variables'; + +@import './embeddable/grid/index'; +@import './embeddable/panel/index'; +@import './embeddable/viewport/index'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx b/src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.test.tsx similarity index 97% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.test.tsx index 0fa34817bee862..f8c05170e8f672 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.test.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_api'; +import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_plugin'; import { ExpandPanelAction } from './expand_panel_action'; import { DashboardContainer } from '../embeddable'; import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers'; @@ -27,7 +27,7 @@ import { ContactCardEmbeddable, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, -} from '../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; +} from '../embeddable_plugin_test_samples'; import { DashboardOptions } from '../embeddable/dashboard_container_factory'; const embeddableFactories = new Map(); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx b/src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.tsx similarity index 93% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.tsx index f05d0b0efc2ee1..68f68f8a53bccd 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/expand_panel_action.tsx @@ -18,12 +18,9 @@ */ import { i18n } from '@kbn/i18n'; -import { IEmbeddable } from '../../../../../../embeddable_api/public/np_ready/public'; +import { IEmbeddable } from '../embeddable_plugin'; +import { IAction, IncompatibleActionError } from '../ui_actions_plugin'; import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; -import { - IAction, - IncompatibleActionError, -} from '../../../../../../../../../src/plugins/ui_actions/public'; export const EXPAND_PANEL_ACTION = 'togglePanel'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/index.ts b/src/plugins/dashboard_embeddable_container/public/actions/index.ts similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/index.ts rename to src/plugins/dashboard_embeddable_container/public/actions/index.ts diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/open_replace_panel_flyout.tsx b/src/plugins/dashboard_embeddable_container/public/actions/open_replace_panel_flyout.tsx similarity index 78% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/open_replace_panel_flyout.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/open_replace_panel_flyout.tsx index b6652caf3ab837..cb354375e7a683 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/open_replace_panel_flyout.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/open_replace_panel_flyout.tsx @@ -17,26 +17,32 @@ * under the License. */ import React from 'react'; -import { CoreStart } from 'src/core/public'; +import { CoreStart } from '../../../../core/public'; import { ReplacePanelFlyout } from './replace_panel_flyout'; - import { IEmbeddable, EmbeddableInput, EmbeddableOutput, -} from '../../../../../../embeddable_api/public/np_ready/public'; - -import { IContainer } from '../../../../../../embeddable_api/public/np_ready/public'; -import { NotificationsStart } from '../../../../../../../../core/public'; + Start as EmbeddableStart, + IContainer, +} from '../embeddable_plugin'; export async function openReplacePanelFlyout(options: { embeddable: IContainer; core: CoreStart; savedObjectFinder: React.ComponentType; - notifications: NotificationsStart; + notifications: CoreStart['notifications']; panelToRemove: IEmbeddable; + getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; }) { - const { embeddable, core, panelToRemove, savedObjectFinder, notifications } = options; + const { + embeddable, + core, + panelToRemove, + savedObjectFinder, + notifications, + getEmbeddableFactories, + } = options; const flyoutSession = core.overlays.openFlyout( , { 'data-test-subj': 'replacePanelFlyout', diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.test.tsx b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.test.tsx similarity index 83% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.test.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.test.tsx index e1d2e3609570c0..de29e1dec85a86 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.test.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_api'; +import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_plugin'; import { ReplacePanelAction } from './replace_panel_action'; import { DashboardContainer } from '../embeddable'; import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers'; @@ -27,7 +27,7 @@ import { ContactCardEmbeddable, ContactCardEmbeddableInput, ContactCardEmbeddableOutput, -} from '../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; +} from '../embeddable_plugin_test_samples'; import { DashboardOptions } from '../embeddable/dashboard_container_factory'; const embeddableFactories = new Map(); @@ -35,6 +35,7 @@ embeddableFactories.set( CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) ); +const getEmbeddableFactories = () => embeddableFactories.values(); let container: DashboardContainer; let embeddable: ContactCardEmbeddable; @@ -82,7 +83,12 @@ test('Executes the replace panel action', async () => { let core: any; let SavedObjectFinder: any; let notifications: any; - const action = new ReplacePanelAction(core, SavedObjectFinder, notifications); + const action = new ReplacePanelAction( + core, + SavedObjectFinder, + notifications, + getEmbeddableFactories + ); action.execute({ embeddable }); }); @@ -90,7 +96,12 @@ test('Is not compatible when embeddable is not in a dashboard container', async let core: any; let SavedObjectFinder: any; let notifications: any; - const action = new ReplacePanelAction(core, SavedObjectFinder, notifications); + const action = new ReplacePanelAction( + core, + SavedObjectFinder, + notifications, + getEmbeddableFactories + ); expect( await action.isCompatible({ embeddable: new ContactCardEmbeddable( @@ -105,7 +116,12 @@ test('Execute throws an error when called with an embeddable not in a parent', a let core: any; let SavedObjectFinder: any; let notifications: any; - const action = new ReplacePanelAction(core, SavedObjectFinder, notifications); + const action = new ReplacePanelAction( + core, + SavedObjectFinder, + notifications, + getEmbeddableFactories + ); async function check() { await action.execute({ embeddable: container }); } @@ -116,7 +132,12 @@ test('Returns title', async () => { let core: any; let SavedObjectFinder: any; let notifications: any; - const action = new ReplacePanelAction(core, SavedObjectFinder, notifications); + const action = new ReplacePanelAction( + core, + SavedObjectFinder, + notifications, + getEmbeddableFactories + ); expect(action.getDisplayName({ embeddable })).toBeDefined(); }); @@ -124,6 +145,11 @@ test('Returns an icon', async () => { let core: any; let SavedObjectFinder: any; let notifications: any; - const action = new ReplacePanelAction(core, SavedObjectFinder, notifications); + const action = new ReplacePanelAction( + core, + SavedObjectFinder, + notifications, + getEmbeddableFactories + ); expect(action.getIconType({ embeddable })).toBeDefined(); }); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.tsx b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.tsx similarity index 87% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.tsx index f36efc498b15f1..f6d2fcbcd57fd4 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_action.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_action.tsx @@ -18,15 +18,10 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreStart } from 'src/core/public'; - -import { IEmbeddable, ViewMode } from '../../../../../../embeddable_api/public/np_ready/public'; +import { CoreStart } from '../../../../core/public'; +import { IEmbeddable, ViewMode, Start as EmbeddableStart } from '../embeddable_plugin'; import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; -import { - IAction, - IncompatibleActionError, -} from '../../../../../../../../plugins/ui_actions/public'; -import { NotificationsStart } from '../../../../../../../../core/public'; +import { IAction, IncompatibleActionError } from '../ui_actions_plugin'; import { openReplacePanelFlyout } from './open_replace_panel_flyout'; export const REPLACE_PANEL_ACTION = 'replacePanel'; @@ -47,7 +42,8 @@ export class ReplacePanelAction implements IAction { constructor( private core: CoreStart, private savedobjectfinder: React.ComponentType, - private notifications: NotificationsStart + private notifications: CoreStart['notifications'], + private getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'] ) {} public getDisplayName({ embeddable }: ActionContext) { @@ -89,6 +85,7 @@ export class ReplacePanelAction implements IAction { savedObjectFinder: this.savedobjectfinder, notifications: this.notifications, panelToRemove: view, + getEmbeddableFactories: this.getEmbeddableFactories, }); } } diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_flyout.tsx b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx similarity index 90% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_flyout.tsx rename to src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx index 0e738556372ce0..02e5f45fae3bd6 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard_embeddable_container/public/actions/replace_panel_flyout.tsx @@ -19,10 +19,6 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; - -import { NotificationsStart } from 'src/core/public'; -import { DashboardPanelState } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; - import { EuiFlyout, EuiFlyoutBody, @@ -30,15 +26,15 @@ import { EuiTitle, EuiGlobalToastListToast as Toast, } from '@elastic/eui'; - -import { IContainer } from '../../../../../../embeddable_api/public/np_ready/public'; +import { DashboardPanelState } from '../embeddable'; +import { NotificationsStart } from '../../../../core/public'; import { + IContainer, IEmbeddable, EmbeddableInput, EmbeddableOutput, -} from '../../../../../../embeddable_api/public/np_ready/public'; - -import { start } from '../../../../../../embeddable_api/public/np_ready/public/legacy'; + Start as EmbeddableStart, +} from '../embeddable_plugin'; interface Props { container: IContainer; @@ -46,6 +42,7 @@ interface Props { onClose: () => void; notifications: NotificationsStart; panelToRemove: IEmbeddable; + getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; } export class ReplacePanelFlyout extends React.Component { @@ -117,7 +114,7 @@ export class ReplacePanelFlyout extends React.Component { defaultMessage: 'No matching objects found.', } )} - savedObjectMetaData={[...start.getEmbeddableFactories()] + savedObjectMetaData={[...this.props.getEmbeddableFactories()] .filter( embeddableFactory => Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_constants.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_constants.ts similarity index 94% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_constants.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_constants.ts index 941ddd3c5efecf..34cd8d42a11880 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_constants.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_constants.ts @@ -21,3 +21,4 @@ export const DASHBOARD_GRID_COLUMN_COUNT = 48; export const DASHBOARD_GRID_HEIGHT = 20; export const DEFAULT_PANEL_WIDTH = DASHBOARD_GRID_COLUMN_COUNT / 2; export const DEFAULT_PANEL_HEIGHT = 15; +export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.test.tsx similarity index 97% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.test.tsx index 06bc696b95193d..770c46c62e42f6 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.test.tsx @@ -20,7 +20,7 @@ // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; import { nextTick } from 'test_utils/enzyme_helpers'; -import { isErrorEmbeddable, ViewMode, EmbeddableFactory } from '../embeddable_api'; +import { isErrorEmbeddable, ViewMode, EmbeddableFactory } from '../embeddable_plugin'; import { DashboardContainer, DashboardContainerOptions } from './dashboard_container'; import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers'; import { @@ -29,7 +29,7 @@ import { ContactCardEmbeddableInput, ContactCardEmbeddable, ContactCardEmbeddableOutput, -} from '../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; +} from '../embeddable_plugin_test_samples'; const options: DashboardContainerOptions = { application: {} as any, diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx similarity index 83% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx index 919995f544960d..8b258f35584388 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx @@ -20,9 +20,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { Filter } from '@kbn/es-query'; -import { RefreshInterval, TimeRange } from 'src/plugins/data/public'; -import { IUiActionsStart } from '../../../../../../../../plugins/ui_actions/public'; +import { RefreshInterval, TimeRange, Query } from '../../../data/public'; +import { CoreStart } from '../../../../core/public'; +import { IUiActionsStart } from '../ui_actions_plugin'; import { Container, ContainerInput, @@ -30,24 +30,23 @@ import { ViewMode, EmbeddableFactory, IEmbeddable, -} from '../../../../../../embeddable_api/public/np_ready/public'; -import { DASHBOARD_CONTAINER_TYPE } from './dashboard_container_factory'; + Start as EmbeddableStartContract, +} from '../embeddable_plugin'; +import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; import { createPanelState } from './panel'; import { DashboardPanelState } from './types'; import { DashboardViewport } from './viewport/dashboard_viewport'; -import { Query } from '../../../../../../data/public'; -import { CoreStart } from '../../../../../../../../core/public'; -import { Start as InspectorStartContract } from '../../../../../../../../plugins/inspector/public'; -import { Start as EmbeddableStartContract } from '../../../../../../embeddable_api/public/np_ready/public'; +import { Start as InspectorStartContract } from '../../../inspector/public'; +import { esFilters } from '../../../../plugins/data/public'; import { + KibanaContextProvider, KibanaReactContext, KibanaReactContextValue, - KibanaContextProvider, -} from '../../../../../../../../plugins/kibana_react/public'; +} from '../../../kibana_react/public'; export interface DashboardContainerInput extends ContainerInput { viewMode: ViewMode; - filters: Filter[]; + filters: esFilters.Filter[]; query: Query; timeRange: TimeRange; refreshConfig?: RefreshInterval; @@ -66,7 +65,7 @@ interface IndexSignature { } export interface InheritedChildInput extends IndexSignature { - filters: Filter[]; + filters: esFilters.Filter[]; query: Query; timeRange: TimeRange; refreshConfig?: RefreshInterval; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container_factory.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container_factory.tsx similarity index 91% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container_factory.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container_factory.tsx index 29ce9bcb0da2e8..c8a2837fd77d0b 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container_factory.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container_factory.tsx @@ -18,17 +18,21 @@ */ import { i18n } from '@kbn/i18n'; -import { SavedObjectAttributes } from '../../../../../../../../core/server'; +import { SavedObjectAttributes } from '../../../../core/public'; import { SavedObjectMetaData } from '../types'; -import { ContainerOutput, EmbeddableFactory, ErrorEmbeddable, Container } from '../embeddable_api'; +import { + ContainerOutput, + EmbeddableFactory, + ErrorEmbeddable, + Container, +} from '../embeddable_plugin'; import { DashboardContainer, DashboardContainerInput, DashboardContainerOptions, } from './dashboard_container'; import { DashboardCapabilities } from '../types'; - -export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; +import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; export interface DashboardOptions extends DashboardContainerOptions { savedObjectMetaData?: SavedObjectMetaData; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/_dashboard_grid.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/_dashboard_grid.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/_index.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_index.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/_index.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/grid/_index.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.test.tsx similarity index 96% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.test.tsx index 386aae9ddcf78e..e4338dc89153d3 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.test.tsx @@ -23,15 +23,15 @@ import sizeMe from 'react-sizeme'; import React from 'react'; import { nextTick, mountWithIntl } from 'test_utils/enzyme_helpers'; import { skip } from 'rxjs/operators'; -import { EmbeddableFactory, GetEmbeddableFactory } from '../../embeddable_api'; +import { EmbeddableFactory, GetEmbeddableFactory } from '../../embeddable_plugin'; import { DashboardGrid, DashboardGridProps } from './dashboard_grid'; import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container'; import { getSampleDashboardInput } from '../../test_helpers'; import { CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddableFactory, -} from '../../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; -import { KibanaContextProvider } from '../../../../../../../../../plugins/kibana_react/public'; +} from '../../embeddable_plugin_test_samples'; +import { KibanaContextProvider } from '../../../../kibana_react/public'; let dashboardContainer: DashboardContainer | undefined; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.tsx similarity index 95% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.tsx index 291bef15641f36..40db43427339d9 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/dashboard_grid.tsx @@ -20,19 +20,21 @@ import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; +// @ts-ignore +import sizeMe from 'react-sizeme'; + import { injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import _ from 'lodash'; import React from 'react'; import { Subscription } from 'rxjs'; import ReactGridLayout, { Layout } from 'react-grid-layout'; -// @ts-ignore -import sizeMe from 'react-sizeme'; -import { ViewMode, EmbeddableChildPanel } from '../../embeddable_api'; +import { ViewMode, EmbeddableChildPanel } from '../../embeddable_plugin'; import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../dashboard_constants'; -import { DashboardContainer, DashboardReactContextValue } from '../dashboard_container'; import { DashboardPanelState, GridData } from '../types'; -import { withKibana } from '../../../../../../../../../plugins/kibana_react/public'; +import { withKibana } from '../../../../kibana_react/public'; +import { DashboardContainerInput } from '../dashboard_container'; +import { DashboardContainer, DashboardReactContextValue } from '../dashboard_container'; let lastValidGridSize = 0; @@ -174,16 +176,18 @@ class DashboardGridUi extends React.Component { isLayoutInvalid, }); - this.subscription = this.props.container.getInput$().subscribe(input => { - if (this.mounted) { - this.setState({ - panels: input.panels, - viewMode: input.viewMode, - useMargins: input.useMargins, - expandedPanelId: input.expandedPanelId, - }); - } - }); + this.subscription = this.props.container + .getInput$() + .subscribe((input: DashboardContainerInput) => { + if (this.mounted) { + this.setState({ + panels: input.panels, + viewMode: input.viewMode, + useMargins: input.useMargins, + expandedPanelId: input.expandedPanelId, + }); + } + }); } public componentWillUnmount() { diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/index.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/index.ts similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/index.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/grid/index.ts diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/index.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/index.ts similarity index 91% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/index.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/index.ts index 2a6a56e3300bcb..58bfd5eedefcb1 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/index.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { DASHBOARD_CONTAINER_TYPE, DashboardContainerFactory } from './dashboard_container_factory'; +export { DashboardContainerFactory } from './dashboard_container_factory'; export { DashboardContainer, DashboardContainerInput } from './dashboard_container'; export { createPanelState } from './panel'; @@ -27,4 +27,5 @@ export { DASHBOARD_GRID_COLUMN_COUNT, DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH, + DASHBOARD_CONTAINER_TYPE, } from './dashboard_constants'; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/_dashboard_panel.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/_dashboard_panel.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/_dashboard_panel.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/panel/_dashboard_panel.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/_index.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/_index.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/_index.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/panel/_index.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.test.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.test.ts similarity index 93% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.test.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.test.ts index e66a25831577c6..8889f4dc275447 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.test.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.test.ts @@ -20,9 +20,8 @@ import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants'; import { DashboardPanelState } from '../types'; import { createPanelState } from './create_panel_state'; -import { EmbeddableInput } from '../../embeddable_api'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { CONTACT_CARD_EMBEDDABLE } from '../../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; +import { EmbeddableInput } from '../../embeddable_plugin'; +import { CONTACT_CARD_EMBEDDABLE } from '../../embeddable_plugin_test_samples'; interface TestInput extends EmbeddableInput { test: string; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.ts similarity index 98% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.ts index 4f3de4a9b2bfb8..7139cddf02b336 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/create_panel_state.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/create_panel_state.ts @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { PanelState, EmbeddableInput } from '../../embeddable_api'; +import { PanelState, EmbeddableInput } from '../../embeddable_plugin'; import { DASHBOARD_GRID_COLUMN_COUNT, DEFAULT_PANEL_HEIGHT, diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/index.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/panel/index.ts similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/panel/index.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/panel/index.ts diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/types.ts b/src/plugins/dashboard_embeddable_container/public/embeddable/types.ts similarity index 94% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/types.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable/types.ts index 6e5257f0d1ee0b..480d03552ca68e 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/types.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/types.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { PanelState, EmbeddableInput } from '../embeddable_api'; +import { PanelState, EmbeddableInput } from '../embeddable_plugin'; export type PanelId = string; export type SavedObjectId = string; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/_dashboard_viewport.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/_dashboard_viewport.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/_index.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_index.scss similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/_index.scss rename to src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_index.scss diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx similarity index 95% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.test.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx index 01bde21f91d3b7..7b83407bf8063c 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx @@ -24,15 +24,15 @@ import { skip } from 'rxjs/operators'; import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n/react'; import { nextTick } from 'test_utils/enzyme_helpers'; -import { EmbeddableFactory } from '../../embeddable_api'; +import { EmbeddableFactory } from '../../embeddable_plugin'; import { DashboardViewport, DashboardViewportProps } from './dashboard_viewport'; import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container'; import { getSampleDashboardInput } from '../../test_helpers'; import { CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddableFactory, -} from '../../../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; -import { KibanaContextProvider } from '../../../../../../../../../plugins/kibana_react/public'; +} from '../../embeddable_plugin_test_samples'; +import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; let dashboardContainer: DashboardContainer | undefined; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx similarity index 95% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.tsx rename to src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx index 59accb225238be..13407e5e33725a 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx @@ -19,10 +19,10 @@ import React from 'react'; import { Subscription } from 'rxjs'; -import { PanelState } from '../../embeddable_api'; +import { PanelState } from '../../embeddable_plugin'; import { DashboardContainer, DashboardReactContextValue } from '../dashboard_container'; import { DashboardGrid } from '../grid'; -import { context } from '../../../../../../../../../plugins/kibana_react/public'; +import { context } from '../../../../kibana_react/public'; export interface DashboardViewportProps { container: DashboardContainer; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable_api.ts b/src/plugins/dashboard_embeddable_container/public/embeddable_plugin.ts similarity index 91% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable_api.ts rename to src/plugins/dashboard_embeddable_container/public/embeddable_plugin.ts index 17911b908d3b3f..30c0ec49751416 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable_api.ts +++ b/src/plugins/dashboard_embeddable_container/public/embeddable_plugin.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from '../../../../../embeddable_api/public/np_ready/public'; +export * from '../../../plugins/embeddable/public'; diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable_plugin_test_samples.ts b/src/plugins/dashboard_embeddable_container/public/embeddable_plugin_test_samples.ts new file mode 100644 index 00000000000000..0e49a94278dfc9 --- /dev/null +++ b/src/plugins/dashboard_embeddable_container/public/embeddable_plugin_test_samples.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. + */ + +// eslint-disable-next-line +export * from '../../../plugins/embeddable/public/lib/test_samples'; diff --git a/src/plugins/dashboard_embeddable_container/public/index.ts b/src/plugins/dashboard_embeddable_container/public/index.ts new file mode 100644 index 00000000000000..73597525105dba --- /dev/null +++ b/src/plugins/dashboard_embeddable_container/public/index.ts @@ -0,0 +1,31 @@ +/* + * 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 { PluginInitializerContext } from '../../../core/public'; +import { DashboardEmbeddableContainerPublicPlugin } from './plugin'; + +export * from './types'; +export * from './actions'; +export * from './embeddable'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new DashboardEmbeddableContainerPublicPlugin(initializerContext); +} + +export { DashboardEmbeddableContainerPublicPlugin as Plugin }; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts b/src/plugins/dashboard_embeddable_container/public/plugin.tsx similarity index 61% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts rename to src/plugins/dashboard_embeddable_container/public/plugin.tsx index bb18d109b0de73..eadf70a36416a6 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts +++ b/src/plugins/dashboard_embeddable_container/public/plugin.tsx @@ -17,11 +17,21 @@ * under the License. */ +/* eslint-disable max-classes-per-file */ + +import * as React from 'react'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { IUiActionsSetup, IUiActionsStart } from '../../../../../../plugins/ui_actions/public'; -import { CONTEXT_MENU_TRIGGER, Plugin as EmbeddablePlugin } from './lib/embeddable_api'; -import { ExpandPanelAction, ReplacePanelAction, DashboardContainerFactory } from './lib'; -import { Start as InspectorStartContract } from '../../../../../../plugins/inspector/public'; +import { IUiActionsSetup, IUiActionsStart } from '../../../plugins/ui_actions/public'; +import { CONTEXT_MENU_TRIGGER, Plugin as EmbeddablePlugin } from './embeddable_plugin'; +import { ExpandPanelAction, ReplacePanelAction } from '.'; +import { DashboardContainerFactory } from './embeddable/dashboard_container_factory'; +import { Start as InspectorStartContract } from '../../../plugins/inspector/public'; +import { + SavedObjectFinder as SavedObjectFinderUi, + SavedObjectFinderProps, + ExitFullScreenButton as ExitFullScreenButtonUi, + ExitFullScreenButtonProps, +} from '../../../plugins/kibana_react/public'; interface SetupDependencies { embeddable: ReturnType; @@ -32,10 +42,6 @@ interface StartDependencies { embeddable: ReturnType; inspector: InspectorStartContract; uiActions: IUiActionsStart; - __LEGACY: { - SavedObjectFinder: React.ComponentType; - ExitFullScreenButton: React.ComponentType; - }; } export type Setup = void; @@ -53,12 +59,35 @@ export class DashboardEmbeddableContainerPublicPlugin public start(core: CoreStart, plugins: StartDependencies): Start { const { application, notifications, overlays } = core; - const { embeddable, inspector, __LEGACY, uiActions } = plugins; + const { embeddable, inspector, uiActions } = plugins; + + const SavedObjectFinder: React.FC< + Exclude + > = props => ( + + ); + + const useHideChrome = () => { + React.useEffect(() => { + core.chrome.setIsVisible(false); + return () => core.chrome.setIsVisible(true); + }, []); + }; + + const ExitFullScreenButton: React.FC = props => { + useHideChrome(); + return ; + }; const changeViewAction = new ReplacePanelAction( core, - __LEGACY.SavedObjectFinder, - notifications + SavedObjectFinder, + notifications, + plugins.embeddable.getEmbeddableFactories ); uiActions.registerAction(changeViewAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction.id); @@ -69,8 +98,8 @@ export class DashboardEmbeddableContainerPublicPlugin overlays, embeddable, inspector, - SavedObjectFinder: __LEGACY.SavedObjectFinder, - ExitFullScreenButton: __LEGACY.ExitFullScreenButton, + SavedObjectFinder, + ExitFullScreenButton, uiActions, }); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/test_helpers/get_sample_dashboard_input.ts b/src/plugins/dashboard_embeddable_container/public/test_helpers/get_sample_dashboard_input.ts similarity index 96% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/test_helpers/get_sample_dashboard_input.ts rename to src/plugins/dashboard_embeddable_container/public/test_helpers/get_sample_dashboard_input.ts index 98eb0636f9aad7..09478d8e8af354 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/test_helpers/get_sample_dashboard_input.ts +++ b/src/plugins/dashboard_embeddable_container/public/test_helpers/get_sample_dashboard_input.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ViewMode, EmbeddableInput } from '../embeddable_api'; +import { ViewMode, EmbeddableInput } from '../embeddable_plugin'; import { DashboardContainerInput, DashboardPanelState } from '../embeddable'; export function getSampleDashboardInput( diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/test_helpers/index.ts b/src/plugins/dashboard_embeddable_container/public/test_helpers/index.ts similarity index 100% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/test_helpers/index.ts rename to src/plugins/dashboard_embeddable_container/public/test_helpers/index.ts diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx b/src/plugins/dashboard_embeddable_container/public/tests/dashboard_container.test.tsx similarity index 83% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx rename to src/plugins/dashboard_embeddable_container/public/tests/dashboard_container.test.tsx index 6cf409581b76d0..6a3b69af60d6b3 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx +++ b/src/plugins/dashboard_embeddable_container/public/tests/dashboard_container.test.tsx @@ -23,28 +23,24 @@ import React from 'react'; import { mount } from 'enzyme'; import { nextTick } from 'test_utils/enzyme_helpers'; import { I18nProvider } from '@kbn/i18n/react'; -import { ViewMode, CONTEXT_MENU_TRIGGER, EmbeddablePanel } from '../lib/embeddable_api'; -import { - DashboardContainer, - DashboardContainerOptions, -} from '../lib/embeddable/dashboard_container'; -import { getSampleDashboardInput } from '../lib/test_helpers'; +import { ViewMode, CONTEXT_MENU_TRIGGER, EmbeddablePanel } from '../embeddable_plugin'; +import { DashboardContainer, DashboardContainerOptions } from '../embeddable/dashboard_container'; +import { getSampleDashboardInput } from '../test_helpers'; import { CONTACT_CARD_EMBEDDABLE, ContactCardEmbeddableFactory, -} from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; -import { ContactCardEmbeddableInput, ContactCardEmbeddable, ContactCardEmbeddableOutput, -} from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; -import { embeddablePluginMock } from '../../../../../embeddable_api/public/np_ready/public/mocks'; -import { createEditModeAction } from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples'; + createEditModeAction, +} from '../embeddable_plugin_test_samples'; +// eslint-disable-next-line +import { embeddablePluginMock } from '../../../embeddable/public/mocks'; // eslint-disable-next-line -import { inspectorPluginMock } from '../../../../../../../plugins/inspector/public/mocks'; -import { KibanaContextProvider } from '../../../../../../../plugins/kibana_react/public'; +import { inspectorPluginMock } from '../../../inspector/public/mocks'; +import { KibanaContextProvider } from '../../../kibana_react/public'; // eslint-disable-next-line -import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks'; +import { uiActionsPluginMock } from '../../../ui_actions/public/mocks'; test('DashboardContainer in edit mode shows edit mode actions', async () => { const inspector = inspectorPluginMock.createStartContract(); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/types.ts b/src/plugins/dashboard_embeddable_container/public/types.ts similarity index 95% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/types.ts rename to src/plugins/dashboard_embeddable_container/public/types.ts index f729573b01dc9e..9c2d6c0ab388de 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/types.ts +++ b/src/plugins/dashboard_embeddable_container/public/types.ts @@ -18,10 +18,7 @@ */ import { IconType } from '@elastic/eui'; -import { - SavedObject as SavedObjectType, - SavedObjectAttributes, -} from '../../../../../../../core/server'; +import { SavedObject as SavedObjectType, SavedObjectAttributes } from '../../../core/public'; export interface DashboardCapabilities { showWriteControls: boolean; diff --git a/src/plugins/dashboard_embeddable_container/public/ui_actions_plugin.ts b/src/plugins/dashboard_embeddable_container/public/ui_actions_plugin.ts new file mode 100644 index 00000000000000..c8778025e77132 --- /dev/null +++ b/src/plugins/dashboard_embeddable_container/public/ui_actions_plugin.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 '../../../plugins/ui_actions/public'; diff --git a/src/plugins/data/common/es_query/__tests__/fields_mock.ts b/src/plugins/data/common/es_query/__tests__/fields_mock.ts new file mode 100644 index 00000000000000..83fdf588af00c8 --- /dev/null +++ b/src/plugins/data/common/es_query/__tests__/fields_mock.ts @@ -0,0 +1,320 @@ +/* + * 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 fields = [ + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ssl', + type: 'boolean', + esTypes: ['boolean'], + count: 20, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@timestamp', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'time', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@tags', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'utc_time', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'phpmemory', + type: 'number', + esTypes: ['integer'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ip', + type: 'ip', + esTypes: ['ip'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'request_body', + type: 'attachment', + esTypes: ['attachment'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'point', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'area', + type: 'geo_shape', + esTypes: ['geo_shape'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'hashed', + type: 'murmur3', + esTypes: ['murmur3'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'geo.coordinates', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'extension', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'machine.os', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'machine.os.raw', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + subType: { multi: { parent: 'machine.os' } }, + }, + { + name: 'geo.src', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '_id', + type: 'string', + esTypes: ['_id'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_type', + type: 'string', + esTypes: ['_type'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_source', + type: '_source', + esTypes: ['_source'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-filterable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-sortable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'custom_user_field', + type: 'conflict', + esTypes: ['long', 'text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'script string', + type: 'string', + count: 0, + scripted: true, + script: "'i am a string'", + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script number', + type: 'number', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script date', + type: 'date', + count: 0, + scripted: true, + script: '1234', + lang: 'painless', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script murmur3', + type: 'murmur3', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'nestedField.child', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField' } }, + }, + { + name: 'nestedField.nestedChild.doublyNestedChild', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField.nestedChild' } }, + }, +]; + +export const getField = (name: string) => fields.find(field => field.name === name); diff --git a/packages/kbn-es-query/src/filters/lib/custom_filter.ts b/src/plugins/data/common/es_query/filters/custom_filter.ts similarity index 100% rename from packages/kbn-es-query/src/filters/lib/custom_filter.ts rename to src/plugins/data/common/es_query/filters/custom_filter.ts diff --git a/packages/kbn-es-query/src/filters/lib/exists_filter.ts b/src/plugins/data/common/es_query/filters/exists_filter.ts similarity index 81% rename from packages/kbn-es-query/src/filters/lib/exists_filter.ts rename to src/plugins/data/common/es_query/filters/exists_filter.ts index 5843c25c43cffd..9125048e5f6cd0 100644 --- a/packages/kbn-es-query/src/filters/lib/exists_filter.ts +++ b/src/plugins/data/common/es_query/filters/exists_filter.ts @@ -18,6 +18,7 @@ */ import { Filter, FilterMeta } from './meta_filter'; +import { IndexPattern, Field } from './types'; export type ExistsFilterMeta = FilterMeta; @@ -31,3 +32,14 @@ export type ExistsFilter = Filter & { }; export const isExistsFilter = (filter: any): filter is ExistsFilter => filter && filter.exists; + +export const buildExistsFilter = (field: Field, indexPattern: IndexPattern) => { + return { + meta: { + index: indexPattern.id, + }, + exists: { + field: field.name, + }, + } as ExistsFilter; +}; diff --git a/packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts b/src/plugins/data/common/es_query/filters/geo_bounding_box_filter.ts similarity index 100% rename from packages/kbn-es-query/src/filters/lib/geo_bounding_box_filter.ts rename to src/plugins/data/common/es_query/filters/geo_bounding_box_filter.ts diff --git a/packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts b/src/plugins/data/common/es_query/filters/geo_polygon_filter.ts similarity index 100% rename from packages/kbn-es-query/src/filters/lib/geo_polygon_filter.ts rename to src/plugins/data/common/es_query/filters/geo_polygon_filter.ts diff --git a/src/plugins/data/common/es_query/filters/index.ts b/src/plugins/data/common/es_query/filters/index.ts new file mode 100644 index 00000000000000..e28ce9ba74975c --- /dev/null +++ b/src/plugins/data/common/es_query/filters/index.ts @@ -0,0 +1,32 @@ +/* + * 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 './custom_filter'; +export * from './exists_filter'; +export * from './geo_bounding_box_filter'; +export * from './geo_polygon_filter'; +export * from './match_all_filter'; +export * from './meta_filter'; +export * from './missing_filter'; +export * from './phrase_filter'; +export * from './phrases_filter'; +export * from './query_string_filter'; +export * from './range_filter'; + +export * from './types'; diff --git a/packages/kbn-es-query/src/filters/lib/match_all_filter.ts b/src/plugins/data/common/es_query/filters/match_all_filter.ts similarity index 100% rename from packages/kbn-es-query/src/filters/lib/match_all_filter.ts rename to src/plugins/data/common/es_query/filters/match_all_filter.ts diff --git a/packages/kbn-es-query/src/filters/lib/meta_filter.ts b/src/plugins/data/common/es_query/filters/meta_filter.ts similarity index 75% rename from packages/kbn-es-query/src/filters/lib/meta_filter.ts rename to src/plugins/data/common/es_query/filters/meta_filter.ts index 8f6aef782cea24..9adfdc4eedcb33 100644 --- a/packages/kbn-es-query/src/filters/lib/meta_filter.ts +++ b/src/plugins/data/common/es_query/filters/meta_filter.ts @@ -55,7 +55,7 @@ export interface LatLon { lon: number; } -export function buildEmptyFilter(isPinned: boolean, index?: string): Filter { +export const buildEmptyFilter = (isPinned: boolean, index?: string): Filter => { const meta: FilterMeta = { disabled: false, negate: false, @@ -65,43 +65,43 @@ export function buildEmptyFilter(isPinned: boolean, index?: string): Filter { const $state: FilterState = { store: isPinned ? FilterStateStore.GLOBAL_STATE : FilterStateStore.APP_STATE, }; + return { meta, $state }; -} +}; -export function isFilterPinned(filter: Filter) { +export const isFilterPinned = (filter: Filter) => { return filter.$state && filter.$state.store === FilterStateStore.GLOBAL_STATE; -} +}; -export function toggleFilterDisabled(filter: Filter) { +export const toggleFilterDisabled = (filter: Filter) => { const disabled = !filter.meta.disabled; const meta = { ...filter.meta, disabled }; + return { ...filter, meta }; -} +}; -export function toggleFilterNegated(filter: Filter) { +export const toggleFilterNegated = (filter: Filter) => { const negate = !filter.meta.negate; const meta = { ...filter.meta, negate }; + return { ...filter, meta }; -} +}; -export function toggleFilterPinned(filter: Filter) { +export const toggleFilterPinned = (filter: Filter) => { const store = isFilterPinned(filter) ? FilterStateStore.APP_STATE : FilterStateStore.GLOBAL_STATE; const $state = { ...filter.$state, store }; + return { ...filter, $state }; -} +}; -export function enableFilter(filter: Filter) { - return !filter.meta.disabled ? filter : toggleFilterDisabled(filter); -} +export const enableFilter = (filter: Filter) => + !filter.meta.disabled ? filter : toggleFilterDisabled(filter); -export function disableFilter(filter: Filter) { - return filter.meta.disabled ? filter : toggleFilterDisabled(filter); -} +export const disableFilter = (filter: Filter) => + filter.meta.disabled ? filter : toggleFilterDisabled(filter); -export function pinFilter(filter: Filter) { - return isFilterPinned(filter) ? filter : toggleFilterPinned(filter); -} +export const pinFilter = (filter: Filter) => + isFilterPinned(filter) ? filter : toggleFilterPinned(filter); -export function unpinFilter(filter: Filter) { - return !isFilterPinned(filter) ? filter : toggleFilterPinned(filter); -} +export const unpinFilter = (filter: Filter) => + !isFilterPinned(filter) ? filter : toggleFilterPinned(filter); diff --git a/packages/kbn-es-query/src/filters/lib/missing_filter.ts b/src/plugins/data/common/es_query/filters/missing_filter.ts similarity index 100% rename from packages/kbn-es-query/src/filters/lib/missing_filter.ts rename to src/plugins/data/common/es_query/filters/missing_filter.ts diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.test.ts b/src/plugins/data/common/es_query/filters/phrase_filter.test.ts new file mode 100644 index 00000000000000..ec13e28c583d18 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/phrase_filter.test.ts @@ -0,0 +1,97 @@ +/* + * 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 { buildInlineScriptForPhraseFilter, buildPhraseFilter } from './phrase_filter'; +import { IndexPattern } from './types'; +import { getField } from '../__tests__/fields_mock'; + +describe('Phrase filter builder', () => { + let indexPattern: IndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'id', + }; + }); + + it('should be a function', () => { + expect(typeof buildPhraseFilter).toBe('function'); + }); + + it('should return a match query filter when passed a standard field', () => { + const field = getField('bytes'); + + expect(buildPhraseFilter(field, 5, indexPattern)).toEqual({ + meta: { + index: 'id', + }, + query: { + match_phrase: { + bytes: 5, + }, + }, + }); + }); + + it('should return a script filter when passed a scripted field', () => { + const field = getField('script number'); + + expect(buildPhraseFilter(field, 5, indexPattern)).toEqual({ + meta: { + index: 'id', + field: 'script number', + }, + script: { + script: { + lang: 'expression', + params: { + value: 5, + }, + source: '(1234) == value', + }, + }, + }); + }); +}); + +describe('buildInlineScriptForPhraseFilter', () => { + it('should wrap painless scripts in a lambda', () => { + const field = { + lang: 'painless', + script: 'return foo;', + }; + + const expected = + `boolean compare(Supplier s, def v) {return s.get() == v;}` + + `compare(() -> { return foo; }, params.value);`; + + expect(buildInlineScriptForPhraseFilter(field)).toBe(expected); + }); + + it('should create a simple comparison for other langs', () => { + const field = { + lang: 'expression', + script: 'doc[bytes].value', + }; + + const expected = `(doc[bytes].value) == value`; + + expect(buildInlineScriptForPhraseFilter(field)).toBe(expected); + }); +}); diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.ts b/src/plugins/data/common/es_query/filters/phrase_filter.ts new file mode 100644 index 00000000000000..15c5c9d4ad2e6d --- /dev/null +++ b/src/plugins/data/common/es_query/filters/phrase_filter.ts @@ -0,0 +1,144 @@ +/* + * 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 { get, isPlainObject } from 'lodash'; +import { Filter, FilterMeta } from './meta_filter'; +import { IndexPattern, Field } from './types'; + +export type PhraseFilterMeta = FilterMeta & { + params?: { + query: string; // The unformatted value + }; + script?: { + script: { + source?: any; + lang?: string; + params: any; + }; + }; + field?: any; + index?: any; +}; + +export type PhraseFilter = Filter & { + meta: PhraseFilterMeta; +}; + +type PhraseFilterValue = string | number | boolean; + +export const isPhraseFilter = (filter: any): filter is PhraseFilter => { + const isMatchPhraseQuery = filter && filter.query && filter.query.match_phrase; + + const isDeprecatedMatchPhraseQuery = + filter && + filter.query && + filter.query.match && + Object.values(filter.query.match).find((params: any) => params.type === 'phrase'); + + return !!(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery); +}; + +export const isScriptedPhraseFilter = (filter: any): filter is PhraseFilter => + Boolean(get(filter, 'script.script.params.value')); + +export const getPhraseFilterField = (filter: PhraseFilter) => { + const queryConfig = filter.query.match_phrase || filter.query.match; + return Object.keys(queryConfig)[0]; +}; + +export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => { + const queryConfig = filter.query.match_phrase || filter.query.match; + const queryValue = Object.values(queryConfig)[0] as any; + return isPlainObject(queryValue) ? queryValue.query : queryValue; +}; + +export const buildPhraseFilter = ( + field: Field, + value: any, + indexPattern: IndexPattern +): PhraseFilter => { + const convertedValue = getConvertedValueForField(field, value); + + if (field.scripted) { + return { + meta: { index: indexPattern.id, field: field.name } as PhraseFilterMeta, + script: getPhraseScript(field, value), + } as PhraseFilter; + } else { + return { + meta: { index: indexPattern.id }, + query: { + match_phrase: { + [field.name]: convertedValue, + }, + }, + } as PhraseFilter; + } +}; + +export const getPhraseScript = (field: Field, value: string) => { + const convertedValue = getConvertedValueForField(field, value); + const script = buildInlineScriptForPhraseFilter(field); + + return { + script: { + source: script, + lang: field.lang, + params: { + value: convertedValue, + }, + }, + }; +}; + +// See https://github.com/elastic/elasticsearch/issues/20941 and https://github.com/elastic/kibana/issues/8677 +// and https://github.com/elastic/elasticsearch/pull/22201 +// for the reason behind this change. Aggs now return boolean buckets with a key of 1 or 0. +export const getConvertedValueForField = (field: Field, value: any) => { + if (typeof value !== 'boolean' && field.type === 'boolean') { + if ([1, 'true'].includes(value)) { + return true; + } else if ([0, 'false'].includes(value)) { + return false; + } else { + throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`); + } + } + return value; +}; + +/** + * Takes a scripted field and returns an inline script appropriate for use in a script query. + * Handles lucene expression and Painless scripts. Other langs aren't guaranteed to generate valid + * scripts. + * + * @param {object} scriptedField A Field object representing a scripted field + * @returns {string} The inline script string + */ +export const buildInlineScriptForPhraseFilter = (scriptedField: any) => { + // We must wrap painless scripts in a lambda in case they're more than a simple expression + if (scriptedField.lang === 'painless') { + return ( + `boolean compare(Supplier s, def v) {return s.get() == v;}` + + `compare(() -> { ${scriptedField.script} }, params.value);` + ); + } else { + return `(${scriptedField.script}) == value`; + } +}; diff --git a/packages/kbn-es-query/src/filters/phrases.js b/src/plugins/data/common/es_query/filters/phrases_filter.ts similarity index 51% rename from packages/kbn-es-query/src/filters/phrases.js rename to src/plugins/data/common/es_query/filters/phrases_filter.ts index f02b3763f37bb7..e4606695c0f6a5 100644 --- a/packages/kbn-es-query/src/filters/phrases.js +++ b/src/plugins/data/common/es_query/filters/phrases_filter.ts @@ -17,47 +17,54 @@ * under the License. */ -import { getPhraseScript } from './phrase'; +import { Filter, FilterMeta } from './meta_filter'; +import { Field, IndexPattern } from './types'; +import { getPhraseScript } from './phrase_filter'; + +export type PhrasesFilterMeta = FilterMeta & { + params: string[]; // The unformatted values + field?: string; +}; + +export type PhrasesFilter = Filter & { + meta: PhrasesFilterMeta; +}; + +export const isPhrasesFilter = (filter: any): filter is PhrasesFilter => + filter && filter.meta.type === 'phrases'; // Creates a filter where the given field matches one or more of the given values // params should be an array of values -export function buildPhrasesFilter(field, params, indexPattern) { +export const buildPhrasesFilter = (field: Field, params: any, indexPattern: IndexPattern) => { const index = indexPattern.id; const type = 'phrases'; const key = field.name; - const value = params - .map(value => format(field, value)) - .join(', '); - const filter = { - meta: { index, type, key, value, params } - }; + const format = (f: Field, value: any) => + f && f.format && f.format.convert ? f.format.convert(value) : value; + + const value = params.map((v: any) => format(field, v)).join(', '); let should; if (field.scripted) { - should = params.map((value) => ({ - script: getPhraseScript(field, value) + should = params.map((v: any) => ({ + script: getPhraseScript(field, v), })); } else { - should = params.map((value) => ({ + should = params.map((v: any) => ({ match_phrase: { - [field.name]: value - } + [field.name]: v, + }, })); } - filter.query = { - bool: { - should, - minimum_should_match: 1 - } - }; - - return filter; -} - -function format(field, value) { - return field && field.format && field.format.convert - ? field.format.convert(value) - : value; -} + return { + meta: { index, type, key, value, params }, + query: { + bool: { + should, + minimum_should_match: 1, + }, + }, + } as PhrasesFilter; +}; diff --git a/src/plugins/data/common/es_query/filters/query_string_filter.test.ts b/src/plugins/data/common/es_query/filters/query_string_filter.test.ts new file mode 100644 index 00000000000000..839e4f6359257e --- /dev/null +++ b/src/plugins/data/common/es_query/filters/query_string_filter.test.ts @@ -0,0 +1,47 @@ +/* + * 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 { buildQueryFilter } from './query_string_filter'; +import { IndexPattern } from './types'; + +describe('Phrase filter builder', () => { + let indexPattern: IndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'id', + }; + }); + + it('should be a function', () => { + expect(typeof buildQueryFilter).toBe('function'); + }); + + it('should return a query filter when passed a standard field', () => { + expect(buildQueryFilter({ foo: 'bar' }, indexPattern.id, '')).toEqual({ + meta: { + alias: '', + index: 'id', + }, + query: { + foo: 'bar', + }, + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/lib/query_string_filter.ts b/src/plugins/data/common/es_query/filters/query_string_filter.ts similarity index 78% rename from packages/kbn-es-query/src/filters/lib/query_string_filter.ts rename to src/plugins/data/common/es_query/filters/query_string_filter.ts index 3b3b97fafba9bc..901dc724aa4e49 100644 --- a/packages/kbn-es-query/src/filters/lib/query_string_filter.ts +++ b/src/plugins/data/common/es_query/filters/query_string_filter.ts @@ -18,6 +18,7 @@ */ import { Filter, FilterMeta } from './meta_filter'; +import { IndexPattern } from './types'; export type QueryStringFilterMeta = FilterMeta; @@ -32,3 +33,17 @@ export type QueryStringFilter = Filter & { export const isQueryStringFilter = (filter: any): filter is QueryStringFilter => filter && filter.query && filter.query.query_string; + +// Creates a filter corresponding to a raw Elasticsearch query DSL object +export const buildQueryFilter = ( + query: QueryStringFilter['query'], + index: IndexPattern, + alias: string +) => + ({ + query, + meta: { + index, + alias, + }, + } as QueryStringFilter); diff --git a/src/plugins/data/common/es_query/filters/range_filter.test.ts b/src/plugins/data/common/es_query/filters/range_filter.test.ts new file mode 100644 index 00000000000000..9008dc2a672944 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/range_filter.test.ts @@ -0,0 +1,174 @@ +/* + * 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 { each } from 'lodash'; +import { buildRangeFilter, RangeFilter } from './range_filter'; +import { IndexPattern, Field } from './types'; +import { getField } from '../__tests__/fields_mock'; + +describe('Range filter builder', () => { + let indexPattern: IndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'id', + }; + }); + + it('should be a function', () => { + expect(typeof buildRangeFilter).toBe('function'); + }); + + it('should return a range filter when passed a standard field', () => { + const field = getField('bytes'); + + expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({ + meta: { + index: 'id', + params: {}, + }, + range: { + bytes: { + gte: 1, + lte: 3, + }, + }, + }); + }); + + it('should return a script filter when passed a scripted field', () => { + const field = getField('script number'); + + expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({ + meta: { + field: 'script number', + index: 'id', + params: {}, + }, + script: { + script: { + lang: 'expression', + source: '(' + field!.script + ')>=gte && (' + field!.script + ')<=lte', + params: { + value: '>=1 <=3', + gte: 1, + lte: 3, + }, + }, + }, + }); + }); + + it('should wrap painless scripts in comparator lambdas', () => { + const field = getField('script date'); + const expected = + `boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))} ` + + `boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}` + + `gte(() -> { ${field!.script} }, params.gte) && ` + + `lte(() -> { ${field!.script} }, params.lte)`; + + const rangeFilter = buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern); + + expect(rangeFilter.script!.script.source).toBe(expected); + }); + + it('should throw an error when gte and gt, or lte and lt are both passed', () => { + const field = getField('script number'); + + expect(() => { + buildRangeFilter(field, { gte: 1, gt: 3 }, indexPattern); + }).toThrowError(); + + expect(() => { + buildRangeFilter(field, { lte: 1, lt: 3 }, indexPattern); + }).toThrowError(); + }); + + it('to use the right operator for each of gte, gt, lt and lte', () => { + const field = getField('script number'); + + each({ gte: '>=', gt: '>', lte: '<=', lt: '<' }, (operator: string, key: any) => { + const params = { + [key]: 5, + }; + + const filter = buildRangeFilter(field, params, indexPattern); + const script = filter.script!.script; + + expect(script.source).toBe('(' + field!.script + ')' + operator + key); + expect(script.params[key]).toBe(5); + expect(script.params.value).toBe(operator + 5); + }); + }); + + describe('when given params where one side is infinite', () => { + let field: Field; + let filter: RangeFilter; + + beforeEach(() => { + field = getField('script number'); + filter = buildRangeFilter(field, { gte: 0, lt: Infinity }, indexPattern); + }); + + describe('returned filter', () => { + it('is a script filter', () => { + expect(filter).toHaveProperty('script'); + }); + + it('contain a param for the finite side', () => { + expect(filter.script!.script.params).toHaveProperty('gte', 0); + }); + + it('does not contain a param for the infinite side', () => { + expect(filter.script!.script.params).not.toHaveProperty('lt'); + }); + + it('does not contain a script condition for the infinite side', () => { + const script = field!.script; + + expect(filter.script!.script.source).toEqual(`(${script})>=gte`); + }); + }); + }); + + describe('when given params where both sides are infinite', () => { + let field: Field; + let filter: RangeFilter; + + beforeEach(() => { + field = getField('script number'); + filter = buildRangeFilter(field, { gte: -Infinity, lt: Infinity }, indexPattern); + }); + + describe('returned filter', () => { + it('is a match_all filter', () => { + expect(filter).not.toHaveProperty('script'); + expect(filter).toHaveProperty('match_all'); + }); + + it('does not contain params', () => { + expect(filter).not.toHaveProperty('params'); + }); + + it('meta field is set to field name', () => { + expect(filter.meta.field).toEqual('script number'); + }); + }); + }); +}); diff --git a/packages/kbn-es-query/src/filters/range.js b/src/plugins/data/common/es_query/filters/range_filter.ts similarity index 51% rename from packages/kbn-es-query/src/filters/range.js rename to src/plugins/data/common/es_query/filters/range_filter.ts index 357f9209c50de3..d7931f191e52b1 100644 --- a/packages/kbn-es-query/src/filters/range.js +++ b/src/plugins/data/common/es_query/filters/range_filter.ts @@ -16,9 +16,12 @@ * specific language governing permissions and limitations * under the License. */ +import { map, reduce, mapValues, get, keys, pick } from 'lodash'; +import { Filter, FilterMeta } from './meta_filter'; +import { Field, IndexPattern } from './types'; -import _ from 'lodash'; const OPERANDS_IN_RANGE = 2; + const operators = { gt: '>', gte: '>=', @@ -39,33 +42,85 @@ const dateComparators = { lt: 'boolean lt(Supplier s, def v) {return s.get().toInstant().isBefore(Instant.parse(v))}', }; -function formatValue(field, params) { - return _.map(params, (val, key) => operators[key] + format(field, val)).join(' '); +export interface RangeFilterParams { + from?: number | string; + to?: number | string; + gt?: number | string; + lt?: number | string; + gte?: number | string; + lte?: number | string; + format?: string; } +const hasRangeKeys = (params: RangeFilterParams) => + Boolean( + keys(params).find((key: string) => ['gte', 'gt', 'lte', 'lt', 'from', 'to'].includes(key)) + ); + +export type RangeFilterMeta = FilterMeta & { + params: RangeFilterParams; + field?: any; + formattedValue?: string; +}; + +export type RangeFilter = Filter & { + meta: RangeFilterMeta; + script?: { + script: { + params: any; + lang: string; + source: any; + }; + }; + match_all?: any; + range: { [key: string]: RangeFilterParams }; +}; + +export const isRangeFilter = (filter: any): filter is RangeFilter => filter && filter.range; + +export const isScriptedRangeFilter = (filter: any): filter is RangeFilter => { + const params: RangeFilterParams = get(filter, 'script.script.params', {}); + + return hasRangeKeys(params); +}; + +const formatValue = (field: Field, params: any[]) => + map(params, (val: any, key: string) => get(operators, key) + format(field, val)).join(' '); + +const format = (field: Field, value: any) => + field && field.format && field.format.convert ? field.format.convert(value) : value; + // Creates a filter where the value for the given field is in the given range // params should be an object containing `lt`, `lte`, `gt`, and/or `gte` -export function buildRangeFilter(field, params, indexPattern, formattedValue) { - const filter = { meta: { index: indexPattern.id } }; - if (formattedValue) filter.meta.formattedValue = formattedValue; +export const buildRangeFilter = ( + field: Field, + params: RangeFilterParams, + indexPattern: IndexPattern, + formattedValue?: string +): RangeFilter => { + const filter: any = { meta: { index: indexPattern.id, params: {} } }; + + if (formattedValue) { + filter.meta.formattedValue = formattedValue; + } - params = _.mapValues(params, (value) => { - return (field.type === 'number') ? parseFloat(value) : value; - }); + params = mapValues(params, value => (field.type === 'number' ? parseFloat(value) : value)); if ('gte' in params && 'gt' in params) throw new Error('gte and gt are mutually exclusive'); if ('lte' in params && 'lt' in params) throw new Error('lte and lt are mutually exclusive'); - const totalInfinite = ['gt', 'lt'].reduce((totalInfinite, op) => { + const totalInfinite = ['gt', 'lt'].reduce((acc: number, op: any) => { const key = op in params ? op : `${op}e`; - const isInfinite = Math.abs(params[key]) === Infinity; + const isInfinite = Math.abs(get(params, key)) === Infinity; if (isInfinite) { - totalInfinite++; + acc++; + + // @ts-ignore delete params[key]; } - return totalInfinite; + return acc; }, 0); if (totalInfinite === OPERANDS_IN_RANGE) { @@ -81,25 +136,29 @@ export function buildRangeFilter(field, params, indexPattern, formattedValue) { filter.range[field.name] = params; } - return filter; -} + return filter as RangeFilter; +}; -export function getRangeScript(field, params) { - const knownParams = _.pick(params, (val, key) => { - return key in operators; - }); - let script = _.map(knownParams, function (val, key) { - return '(' + field.script + ')' + operators[key] + key; - }).join(' && '); +export const getRangeScript = (field: IndexPattern, params: RangeFilterParams) => { + const knownParams = pick(params, (val, key: any) => key in operators); + let script = map( + knownParams, + (val: any, key: string) => '(' + field.script + ')' + get(operators, key) + key + ).join(' && '); // We must wrap painless scripts in a lambda in case they're more than a simple expression if (field.lang === 'painless') { const comp = field.type === 'date' ? dateComparators : comparators; - const currentComparators = _.reduce(knownParams, (acc, val, key) => acc.concat(comp[key]), []).join(' '); + const currentComparators = reduce( + knownParams, + (acc, val, key) => acc.concat(get(comp, key)), + [] + ).join(' '); - const comparisons = _.map(knownParams, function (val, key) { - return `${key}(() -> { ${field.script} }, params.${key})`; - }).join(' && '); + const comparisons = map( + knownParams, + (val, key) => `${key}(() -> { ${field.script} }, params.${key})` + ).join(' && '); script = `${currentComparators}${comparisons}`; } @@ -108,14 +167,7 @@ export function getRangeScript(field, params) { script: { source: script, params: knownParams, - lang: field.lang - } + lang: field.lang, + }, }; -} - -function format(field, value) { - return field && field.format && field.format.convert - ? field.format.convert(value) - : value; -} - +}; diff --git a/src/plugins/data/common/es_query/filters/types.ts b/src/plugins/data/common/es_query/filters/types.ts new file mode 100644 index 00000000000000..28147350619995 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/types.ts @@ -0,0 +1,57 @@ +/* + * 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 { ExistsFilter } from './exists_filter'; +import { GeoBoundingBoxFilter } from './geo_bounding_box_filter'; +import { GeoPolygonFilter } from './geo_polygon_filter'; +import { PhrasesFilter } from './phrases_filter'; +import { PhraseFilter } from './phrase_filter'; +import { RangeFilter } from './range_filter'; +import { MatchAllFilter } from './match_all_filter'; +import { MissingFilter } from './missing_filter'; + +// Any filter associated with a field (used in the filter bar/editor) +export type FieldFilter = + | ExistsFilter + | GeoBoundingBoxFilter + | GeoPolygonFilter + | PhraseFilter + | PhrasesFilter + | RangeFilter + | MatchAllFilter + | MissingFilter; + +export enum FILTERS { + CUSTOM = 'custom', + PHRASES = 'phrases', + PHRASE = 'phrase', + EXISTS = 'exists', + MATCH_ALL = 'match_all', + MISSING = 'missing', + QUERY_STRING = 'query_string', + RANGE = 'range', + GEO_BOUNDING_BOX = 'geo_bounding_box', + GEO_POLYGON = 'geo_polygon', +} + +// We can't import the real types from the data plugin, so need to either duplicate +// them here or figure out another solution, perhaps housing them in this package +// will be replaces after Fieds / IndexPattern will be moved into new platform +export type Field = any; +export type IndexPattern = any; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/index.ts b/src/plugins/data/common/es_query/index.ts similarity index 90% rename from src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/index.ts rename to src/plugins/data/common/es_query/index.ts index 03ba836462c608..88e14a43cfaae2 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/index.ts +++ b/src/plugins/data/common/es_query/index.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ +import * as esFilters from './filters'; -export * from './types'; -export * from './actions'; -export * from './embeddable'; +export { esFilters }; diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts index dca7897bd2766c..42b5a03fcc926a 100644 --- a/src/plugins/data/common/index.ts +++ b/src/plugins/data/common/index.ts @@ -20,5 +20,6 @@ export * from './query'; export * from './field_formats'; export * from './kbn_field_types'; +export * from './es_query'; export * from './types'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 51f26a4bd7f319..32153df69f3678 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -34,5 +34,4 @@ export * from './types'; export { IRequestTypesMap, IResponseTypesMap } from './search'; export * from './search'; - export * from './query'; diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.test.ts b/src/plugins/data/public/query/filter_manager/filter_manager.test.ts index 5092e9e55c2b48..5bee3f7d104283 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.test.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.test.ts @@ -21,10 +21,10 @@ import _ from 'lodash'; import sinon from 'sinon'; import { Subscription } from 'rxjs'; -import { Filter, FilterStateStore } from '@kbn/es-query'; import { FilterManager } from './filter_manager'; import { getFilter } from './test_helpers/get_stub_filter'; import { getFiltersArray } from './test_helpers/get_filters_array'; +import { esFilters } from '../../../common/es_query'; import { coreMock } from '../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); @@ -39,7 +39,7 @@ describe('filter_manager', () => { let updateListener: sinon.SinonSpy; let filterManager: FilterManager; - let readyFilters: Filter[]; + let readyFilters: esFilters.Filter[]; beforeEach(() => { updateListener = sinon.stub(); @@ -82,7 +82,7 @@ describe('filter_manager', () => { test('app state should be set', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.setFilters([f1]); expect(filterManager.getAppFilters()).toHaveLength(1); expect(filterManager.getGlobalFilters()).toHaveLength(0); @@ -96,7 +96,7 @@ describe('filter_manager', () => { test('global state should be set', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.setFilters([f1]); expect(filterManager.getAppFilters()).toHaveLength(0); expect(filterManager.getGlobalFilters()).toHaveLength(1); @@ -110,8 +110,8 @@ describe('filter_manager', () => { test('both states should be set', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); filterManager.setFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(1); expect(filterManager.getGlobalFilters()).toHaveLength(1); @@ -128,8 +128,8 @@ describe('filter_manager', () => { test('set state should override previous state', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); filterManager.setFilters([f1]); filterManager.setFilters([f2]); @@ -150,7 +150,7 @@ describe('filter_manager', () => { test('changing a disabled filter should fire only update event', async function() { const updateStub = jest.fn(); const fetchStub = jest.fn(); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, true, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, true, false, 'age', 34); filterManager.setFilters([f1]); @@ -175,7 +175,7 @@ describe('filter_manager', () => { describe('add filters', () => { test('app state should accept a single filter', async function() { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.addFilters(f1); const appFilters = filterManager.getAppFilters(); expect(appFilters).toHaveLength(1); @@ -185,8 +185,8 @@ describe('filter_manager', () => { }); test('app state should accept array', async () => { - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'female'); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'female'); filterManager.addFilters([f1]); filterManager.addFilters([f2]); const appFilters = filterManager.getAppFilters(); @@ -197,7 +197,7 @@ describe('filter_manager', () => { test('global state should accept a single filer', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); filterManager.addFilters(f1); expect(filterManager.getAppFilters()).toHaveLength(0); const globalFilters = filterManager.getGlobalFilters(); @@ -207,8 +207,14 @@ describe('filter_manager', () => { }); test('global state should be accept array', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'gender', 'female'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter( + esFilters.FilterStateStore.GLOBAL_STATE, + false, + false, + 'gender', + 'female' + ); filterManager.addFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(0); const globalFilters = filterManager.getGlobalFilters(); @@ -218,8 +224,14 @@ describe('filter_manager', () => { test('add multiple filters at once', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'gender', 'female'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter( + esFilters.FilterStateStore.GLOBAL_STATE, + false, + false, + 'gender', + 'female' + ); filterManager.addFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(0); expect(filterManager.getGlobalFilters()).toHaveLength(2); @@ -228,8 +240,8 @@ describe('filter_manager', () => { test('add same filter to global and app', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.addFilters([f1, f2]); // FILTER SHOULD BE ADDED ONLY ONCE, TO GLOBAL @@ -240,8 +252,8 @@ describe('filter_manager', () => { test('add same filter with different values to global and app', async () => { updateSubscription = filterManager.getUpdates$().subscribe(updateListener); - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); filterManager.addFilters([f1, f2]); // FILTER SHOULD BE ADDED TWICE @@ -251,7 +263,7 @@ describe('filter_manager', () => { }); test('add filter with no state, and force pin', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); f1.$state = undefined; filterManager.addFilters([f1], true); @@ -260,12 +272,12 @@ describe('filter_manager', () => { const f1Output = filterManager.getFilters()[0]; expect(f1Output.$state).toBeDefined(); if (f1Output.$state) { - expect(f1Output.$state.store).toBe(FilterStateStore.GLOBAL_STATE); + expect(f1Output.$state.store).toBe(esFilters.FilterStateStore.GLOBAL_STATE); } }); test('add filter with no state, and dont force pin', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 38); f1.$state = undefined; filterManager.addFilters([f1], false); @@ -274,7 +286,7 @@ describe('filter_manager', () => { const f1Output = filterManager.getFilters()[0]; expect(f1Output.$state).toBeDefined(); if (f1Output.$state) { - expect(f1Output.$state.store).toBe(FilterStateStore.APP_STATE); + expect(f1Output.$state.store).toBe(esFilters.FilterStateStore.APP_STATE); } }); @@ -286,11 +298,11 @@ describe('filter_manager', () => { // global filters should be listed first let res = filterManager.getFilters(); expect(res).toHaveLength(2); - expect(res[0].$state && res[0].$state.store).toEqual(FilterStateStore.GLOBAL_STATE); + expect(res[0].$state && res[0].$state.store).toEqual(esFilters.FilterStateStore.GLOBAL_STATE); expect(res[0].meta.disabled).toEqual(filters[1].meta.disabled); expect(res[0].query).toEqual(filters[1].query); - expect(res[1].$state && res[1].$state.store).toEqual(FilterStateStore.APP_STATE); + expect(res[1].$state && res[1].$state.store).toEqual(esFilters.FilterStateStore.APP_STATE); expect(res[1].meta.disabled).toEqual(filters[0].meta.disabled); expect(res[1].query).toEqual(filters[0].query); @@ -310,7 +322,7 @@ describe('filter_manager', () => { const res = filterManager.getFilters(); expect(res).toHaveLength(3); _.each(res, function(filter) { - expect(filter.$state && filter.$state.store).toBe(FilterStateStore.GLOBAL_STATE); + expect(filter.$state && filter.$state.store).toBe(esFilters.FilterStateStore.GLOBAL_STATE); }); }); @@ -393,7 +405,7 @@ describe('filter_manager', () => { }); test('should de-dupe global filters being set', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); const f2 = _.cloneDeep(f1); filterManager.setFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(0); @@ -402,7 +414,7 @@ describe('filter_manager', () => { }); test('should de-dupe app filters being set', async () => { - const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34); + const f1 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'age', 34); const f2 = _.cloneDeep(f1); filterManager.setFilters([f1, f2]); expect(filterManager.getAppFilters()).toHaveLength(1); @@ -417,7 +429,7 @@ describe('filter_manager', () => { const appFilter = _.cloneDeep(readyFilters[idx]); appFilter.meta.negate = true; appFilter.$state = { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }; filterManager.addFilters(appFilter); const res = filterManager.getFilters(); @@ -434,7 +446,7 @@ describe('filter_manager', () => { const appFilter = _.cloneDeep(readyFilters[1]); appFilter.meta.negate = true; appFilter.$state = { - store: FilterStateStore.APP_STATE, + store: esFilters.FilterStateStore.APP_STATE, }; filterManager.addFilters(appFilter, false); @@ -443,7 +455,7 @@ describe('filter_manager', () => { expect(res).toHaveLength(3); expect( res.filter(function(filter) { - return filter.$state && filter.$state.store === FilterStateStore.GLOBAL_STATE; + return filter.$state && filter.$state.store === esFilters.FilterStateStore.GLOBAL_STATE; }).length ).toBe(3); }); @@ -496,8 +508,8 @@ describe('filter_manager', () => { }); test('remove on full should clean and fire events', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); filterManager.setFilters([f1, f2]); updateSubscription = filterManager.getUpdates$().subscribe(updateListener); @@ -507,9 +519,9 @@ describe('filter_manager', () => { }); test('remove non existing filter should do nothing and not fire events', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); - const f3 = getFilter(FilterStateStore.APP_STATE, false, false, 'country', 'US'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); + const f3 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'country', 'US'); filterManager.setFilters([f1, f2]); expect(filterManager.getFilters()).toHaveLength(2); @@ -520,9 +532,9 @@ describe('filter_manager', () => { }); test('remove existing filter should remove and fire events', async () => { - const f1 = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); - const f2 = getFilter(FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); - const f3 = getFilter(FilterStateStore.APP_STATE, false, false, 'country', 'US'); + const f1 = getFilter(esFilters.FilterStateStore.GLOBAL_STATE, false, false, 'age', 34); + const f2 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'gender', 'FEMALE'); + const f3 = getFilter(esFilters.FilterStateStore.APP_STATE, false, false, 'country', 'US'); filterManager.setFilters([f1, f2, f3]); expect(filterManager.getFilters()).toHaveLength(3); diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index 66b65a40926cb7..f691398fb91d3e 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -17,8 +17,6 @@ * under the License. */ -import { Filter, isFilterPinned, FilterStateStore } from '@kbn/es-query'; - import _ from 'lodash'; import { Subject } from 'rxjs'; @@ -29,9 +27,10 @@ import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; import { uniqFilters } from './lib/uniq_filters'; import { onlyDisabledFiltersChanged } from './lib/only_disabled'; import { PartitionedFilters } from './types'; +import { esFilters } from '../../../common/es_query'; export class FilterManager { - private filters: Filter[] = []; + private filters: esFilters.Filter[] = []; private updated$: Subject = new Subject(); private fetch$: Subject = new Subject(); private uiSettings: UiSettingsClientContract; @@ -40,7 +39,7 @@ export class FilterManager { this.uiSettings = uiSettings; } - private mergeIncomingFilters(partitionedFilters: PartitionedFilters): Filter[] { + private mergeIncomingFilters(partitionedFilters: PartitionedFilters): esFilters.Filter[] { const globalFilters = partitionedFilters.globalFilters; const appFilters = partitionedFilters.appFilters; @@ -61,23 +60,26 @@ export class FilterManager { return FilterManager.mergeFilters(appFilters, globalFilters); } - private static mergeFilters(appFilters: Filter[], globalFilters: Filter[]): Filter[] { + private static mergeFilters( + appFilters: esFilters.Filter[], + globalFilters: esFilters.Filter[] + ): esFilters.Filter[] { return uniqFilters(appFilters.reverse().concat(globalFilters.reverse())).reverse(); } - private static partitionFilters(filters: Filter[]): PartitionedFilters { - const [globalFilters, appFilters] = _.partition(filters, isFilterPinned); + private static partitionFilters(filters: esFilters.Filter[]): PartitionedFilters { + const [globalFilters, appFilters] = _.partition(filters, esFilters.isFilterPinned); return { globalFilters, appFilters, }; } - private handleStateUpdate(newFilters: Filter[]) { + private handleStateUpdate(newFilters: esFilters.Filter[]) { // global filters should always be first - newFilters.sort(({ $state: a }: Filter, { $state: b }: Filter): number => { - return a!.store === FilterStateStore.GLOBAL_STATE && - b!.store !== FilterStateStore.GLOBAL_STATE + newFilters.sort(({ $state: a }: esFilters.Filter, { $state: b }: esFilters.Filter): number => { + return a!.store === esFilters.FilterStateStore.GLOBAL_STATE && + b!.store !== esFilters.FilterStateStore.GLOBAL_STATE ? -1 : 1; }); @@ -124,7 +126,7 @@ export class FilterManager { /* Setters */ - public addFilters(filters: Filter[] | Filter, pinFilterStatus?: boolean) { + public addFilters(filters: esFilters.Filter[] | esFilters.Filter, pinFilterStatus?: boolean) { if (!Array.isArray(filters)) { filters = [filters]; } @@ -139,7 +141,10 @@ export class FilterManager { // Set the store of all filters. For now. // In the future, all filters should come in with filter state store already set. - const store = pinFilterStatus ? FilterStateStore.GLOBAL_STATE : FilterStateStore.APP_STATE; + const store = pinFilterStatus + ? esFilters.FilterStateStore.GLOBAL_STATE + : esFilters.FilterStateStore.APP_STATE; + FilterManager.setFiltersStore(filters, store); const mappedFilters = mapAndFlattenFilters(filters); @@ -154,14 +159,14 @@ export class FilterManager { this.handleStateUpdate(newFilters); } - public setFilters(newFilters: Filter[]) { + public setFilters(newFilters: esFilters.Filter[]) { const mappedFilters = mapAndFlattenFilters(newFilters); const newPartitionedFilters = FilterManager.partitionFilters(mappedFilters); const mergedFilters = this.mergeIncomingFilters(newPartitionedFilters); this.handleStateUpdate(mergedFilters); } - public removeFilter(filter: Filter) { + public removeFilter(filter: esFilters.Filter) { const filterIndex = _.findIndex(this.filters, item => { return _.isEqual(item.meta, filter.meta) && _.isEqual(item.query, filter.query); }); @@ -177,8 +182,8 @@ export class FilterManager { this.setFilters([]); } - public static setFiltersStore(filters: Filter[], store: FilterStateStore) { - _.map(filters, (filter: Filter) => { + public static setFiltersStore(filters: esFilters.Filter[], store: esFilters.FilterStateStore) { + _.map(filters, (filter: esFilters.Filter) => { // Override status only for filters that didn't have state in the first place. if (filter.$state === undefined) { filter.$state = { store }; diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts index e8244feb988b6e..6bde6b528d07bd 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts @@ -16,42 +16,48 @@ * specific language governing permissions and limitations * under the License. */ -import { buildQueryFilter, buildEmptyFilter, FilterStateStore } from '@kbn/es-query'; + import { compareFilters } from './compare_filters'; +import { esFilters } from '../../../../common/es_query'; describe('filter manager utilities', () => { describe('compare filters', () => { test('should compare filters', () => { - const f1 = buildQueryFilter( + const f1 = esFilters.buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, - 'index' + 'index', + '' ); - const f2 = buildEmptyFilter(true); + const f2 = esFilters.buildEmptyFilter(true); expect(compareFilters(f1, f2)).toBeFalsy(); }); test('should compare duplicates', () => { - const f1 = buildQueryFilter( + const f1 = esFilters.buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, - 'index' + 'index', + '' ); - const f2 = buildQueryFilter( + const f2 = esFilters.buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, - 'index' + 'index', + '' ); expect(compareFilters(f1, f2)).toBeTruthy(); }); test('should compare duplicates, ignoring meta attributes', () => { - const f1 = buildQueryFilter( + const f1 = esFilters.buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, - 'index1' + 'index1', + '' ); - const f2 = buildQueryFilter( + const f2 = esFilters.buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, - 'index2' + 'index2', + '' ); expect(compareFilters(f1, f2)).toBeTruthy(); @@ -59,12 +65,20 @@ describe('filter manager utilities', () => { test('should compare duplicates, ignoring $state attributes', () => { const f1 = { - $state: { store: FilterStateStore.APP_STATE }, - ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), + $state: { store: esFilters.FilterStateStore.APP_STATE }, + ...esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), }; const f2 = { - $state: { store: FilterStateStore.GLOBAL_STATE }, - ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), + $state: { store: esFilters.FilterStateStore.GLOBAL_STATE }, + ...esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), }; expect(compareFilters(f1, f2)).toBeTruthy(); diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts index 44bc333ae2b4fb..2a7cbe6e3303b5 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts @@ -17,8 +17,8 @@ * under the License. */ -import { Filter, FilterMeta } from '@kbn/es-query'; import { defaults, isEqual, omit } from 'lodash'; +import { esFilters } from '../../../../common/es_query'; /** * Compare two filters to see if they match @@ -29,10 +29,14 @@ import { defaults, isEqual, omit } from 'lodash'; * * @returns {bool} Filters are the same */ -export const compareFilters = (first: Filter, second: Filter, comparatorOptions: any = {}) => { +export const compareFilters = ( + first: esFilters.Filter, + second: esFilters.Filter, + comparatorOptions: any = {} +) => { let comparators: any = {}; - const mapFilter = (filter: Filter) => { - const cleaned: FilterMeta = omit(filter, excludedAttributes); + const mapFilter = (filter: esFilters.Filter) => { + const cleaned: esFilters.FilterMeta = omit(filter, excludedAttributes); if (comparators.negate) cleaned.negate = filter.meta && Boolean(filter.meta.negate); if (comparators.disabled) cleaned.disabled = filter.meta && Boolean(filter.meta.disabled); diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts index 75bd9d5dfbd81a..9b493add0886c6 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts @@ -17,19 +17,27 @@ * under the License. */ -import { Filter, buildRangeFilter, FilterStateStore, buildQueryFilter } from '@kbn/es-query'; import { dedupFilters } from './dedup_filters'; +import { esFilters } from '../../../../common/es_query'; describe('filter manager utilities', () => { describe('dedupFilters(existing, filters)', () => { test('should return only filters which are not in the existing', () => { - const existing: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index'), - buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), + const existing: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), + esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), ]; - const filters: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index'), - buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), + const filters: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), + esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), ]; const results = dedupFilters(existing, filters); @@ -38,16 +46,24 @@ describe('filter manager utilities', () => { }); test('should ignore the disabled attribute when comparing ', () => { - const existing: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index'), + const existing: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), { - ...buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), + ...esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index1', + '' + ), meta: { disabled: true, negate: false, alias: null }, }, ]; - const filters: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index'), - buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), + const filters: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), + esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index1', + '' + ), ]; const results = dedupFilters(existing, filters); @@ -56,18 +72,26 @@ describe('filter manager utilities', () => { }); test('should ignore $state attribute', () => { - const existing: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index'), + const existing: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), { - ...buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), - $state: { store: FilterStateStore.APP_STATE }, + ...esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), + $state: { store: esFilters.FilterStateStore.APP_STATE }, }, ]; - const filters: Filter[] = [ - buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index'), + const filters: esFilters.Filter[] = [ + esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), { - ...buildQueryFilter({ match: { _term: { query: 'apache', type: 'phrase' } } }, 'index'), - $state: { store: FilterStateStore.GLOBAL_STATE }, + ...esFilters.buildQueryFilter( + { match: { _term: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), + $state: { store: esFilters.FilterStateStore.GLOBAL_STATE }, }, ]; const results = dedupFilters(existing, filters); diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts index 9565cbd80b7791..6d6f49cb5e8338 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts @@ -17,9 +17,9 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { filter, find } from 'lodash'; import { compareFilters } from './compare_filters'; +import { esFilters } from '../../../../../../plugins/data/public'; /** * Combine 2 filter collections, removing duplicates @@ -31,8 +31,8 @@ import { compareFilters } from './compare_filters'; * @returns {object} An array of filters that were not in existing */ export const dedupFilters = ( - existingFilters: Filter[], - filters: Filter[], + existingFilters: esFilters.Filter[], + filters: esFilters.Filter[], comparatorOptions: any = {} ) => { if (!Array.isArray(filters)) { @@ -41,8 +41,8 @@ export const dedupFilters = ( return filter( filters, - (f: Filter) => - !find(existingFilters, (existingFilter: Filter) => + (f: esFilters.Filter) => + !find(existingFilters, (existingFilter: esFilters.Filter) => compareFilters(existingFilter, f, comparatorOptions) ) ); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts index c0c509634aba2c..dfe3a093c66146 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts @@ -18,8 +18,8 @@ */ import sinon from 'sinon'; -import { Filter, buildEmptyFilter } from '@kbn/es-query'; import { generateMappingChain } from './generate_mapping_chain'; +import { esFilters } from '../../../../../../plugins/data/public'; describe('filter manager utilities', () => { let mapping: any; @@ -32,7 +32,7 @@ describe('filter manager utilities', () => { describe('generateMappingChain()', () => { test('should create a chaining function which calls the next function if the error is thrown', async () => { - const filter: Filter = buildEmptyFilter(true); + const filter = esFilters.buildEmptyFilter(true); mapping.throws(filter); next.returns('good'); @@ -45,7 +45,7 @@ describe('filter manager utilities', () => { }); test('should create a chaining function which DOES NOT call the next function if the result is returned', async () => { - const filter: Filter = buildEmptyFilter(true); + const filter = esFilters.buildEmptyFilter(true); mapping.returns('good'); next.returns('bad'); @@ -57,7 +57,7 @@ describe('filter manager utilities', () => { }); test('should resolve result for the mapping function', async () => { - const filter: Filter = buildEmptyFilter(true); + const filter = esFilters.buildEmptyFilter(true); mapping.returns({ key: 'test', value: 'example' }); @@ -70,7 +70,7 @@ describe('filter manager utilities', () => { test('should call the mapping function with the argument to the chain', async () => { // @ts-ignore - const filter: Filter = { test: 'example' }; + const filter: esFilters.Filter = { test: 'example' }; mapping.returns({ key: 'test', value: 'example' }); @@ -84,7 +84,7 @@ describe('filter manager utilities', () => { }); test('should resolve result for the next function', async () => { - const filter: Filter = buildEmptyFilter(true); + const filter = esFilters.buildEmptyFilter(true); mapping.throws(filter); next.returns({ key: 'test', value: 'example' }); @@ -98,7 +98,7 @@ describe('filter manager utilities', () => { }); test('should throw an error if no functions match', async done => { - const filter: Filter = buildEmptyFilter(true); + const filter = esFilters.buildEmptyFilter(true); mapping.throws(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts index 760270edd7170a..b6764389e0db9f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts @@ -16,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; const noop = () => { throw new Error('No mappings have been found for filter.'); }; export const generateMappingChain = (fn: Function, next: Function = noop) => { - return (filter: Filter) => { + return (filter: esFilters.Filter) => { try { return fn(filter); } catch (result) { diff --git a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts index fce2aa0373ebe8..9a0d0d93698f69 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts @@ -17,14 +17,14 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { mapAndFlattenFilters } from './map_and_flatten_filters'; +import { esFilters } from '../../../../../data/public'; describe('filter manager utilities', () => { describe('mapAndFlattenFilters()', () => { let filters: unknown; - function getDisplayName(filter: Filter) { + function getDisplayName(filter: esFilters.Filter) { return typeof filter.meta.value === 'function' ? filter.meta.value() : filter.meta.value; } @@ -45,7 +45,7 @@ describe('filter manager utilities', () => { }); test('should map and flatten the filters', () => { - const results = mapAndFlattenFilters(filters as Filter[]); + const results = mapAndFlattenFilters(filters as esFilters.Filter[]); expect(results).toHaveLength(5); expect(results[0]).toHaveProperty('meta'); diff --git a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts index b350c3957b142d..5326d59f3e32b6 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts @@ -18,9 +18,9 @@ */ import { compact, flatten } from 'lodash'; -import { Filter } from '@kbn/es-query'; import { mapFilter } from './map_filter'; +import { esFilters } from '../../../../../data/public'; -export const mapAndFlattenFilters = (filters: Filter[]) => { - return compact(flatten(filters)).map((item: Filter) => mapFilter(item)); +export const mapAndFlattenFilters = (filters: esFilters.Filter[]) => { + return compact(flatten(filters)).map((item: esFilters.Filter) => mapFilter(item)); }; diff --git a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts index c1d4ebfd3f7fc8..0d115125451eea 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts @@ -17,11 +17,11 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { mapFilter } from './map_filter'; +import { esFilters } from '../../../../../data/public'; describe('filter manager utilities', () => { - function getDisplayName(filter: Filter) { + function getDisplayName(filter: esFilters.Filter) { return typeof filter.meta.value === 'function' ? filter.meta.value() : filter.meta.value; } @@ -31,7 +31,7 @@ describe('filter manager utilities', () => { meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } }, }; - const after = mapFilter(before as Filter); + const after = mapFilter(before as esFilters.Filter); expect(after).toHaveProperty('meta'); expect(after.meta).toHaveProperty('key', '_type'); @@ -43,7 +43,7 @@ describe('filter manager utilities', () => { test('should map exists filters', async () => { const before: any = { meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } }; - const after = mapFilter(before as Filter); + const after = mapFilter(before as esFilters.Filter); expect(after).toHaveProperty('meta'); expect(after.meta).toHaveProperty('key', '@timestamp'); @@ -55,7 +55,7 @@ describe('filter manager utilities', () => { test('should map missing filters', async () => { const before: any = { meta: { index: 'logstash-*' }, missing: { field: '@timestamp' } }; - const after = mapFilter(before as Filter); + const after = mapFilter(before as esFilters.Filter); expect(after).toHaveProperty('meta'); expect(after.meta).toHaveProperty('key', '@timestamp'); @@ -67,7 +67,7 @@ describe('filter manager utilities', () => { test('should map json filter', async () => { const before: any = { meta: { index: 'logstash-*' }, query: { match_all: {} } }; - const after = mapFilter(before as Filter); + const after = mapFilter(before as esFilters.Filter); expect(after).toHaveProperty('meta'); expect(after.meta).toHaveProperty('key', 'query'); @@ -81,7 +81,7 @@ describe('filter manager utilities', () => { const before: any = { meta: { index: 'logstash-*' } }; try { - mapFilter(before as Filter); + mapFilter(before as esFilters.Filter); } catch (e) { expect(e).toBeInstanceOf(Error); expect(e.message).toBe('No mappings have been found for filter.'); diff --git a/src/plugins/data/public/query/filter_manager/lib/map_filter.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts index cda9591e40b33e..2dc855caabfd36 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_filter.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts @@ -17,7 +17,6 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { reduceRight } from 'lodash'; import { mapMatchAll } from './mappers/map_match_all'; @@ -31,8 +30,9 @@ import { mapGeoBoundingBox } from './mappers/map_geo_bounding_box'; import { mapGeoPolygon } from './mappers/map_geo_polygon'; import { mapDefault } from './mappers/map_default'; import { generateMappingChain } from './generate_mapping_chain'; +import { esFilters } from '../../../../../data/public'; -export function mapFilter(filter: Filter) { +export function mapFilter(filter: esFilters.Filter) { /** Mappers **/ // Each mapper is a simple promise function that test if the mapper can diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts index acb6e89711033d..f10766901e5b7b 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts @@ -16,13 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { CustomFilter, buildEmptyFilter, buildQueryFilter } from '@kbn/es-query'; + import { mapDefault } from './map_default'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapDefault()', () => { test('should return the key and value for matching filters', async () => { - const filter: CustomFilter = buildQueryFilter({ match_all: {} }, 'index'); + const filter = esFilters.buildQueryFilter({ match_all: {} }, 'index', ''); const result = mapDefault(filter); expect(result).toHaveProperty('key', 'query'); @@ -30,7 +31,7 @@ describe('filter manager utilities', () => { }); test('should return undefined if there is no valid key', async () => { - const filter = buildEmptyFilter(true) as CustomFilter; + const filter = esFilters.buildEmptyFilter(true); try { mapDefault(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts index 70c191879c22e9..fd84c5c742589b 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts @@ -17,15 +17,15 @@ * under the License. */ -import { Filter, FILTERS } from '@kbn/es-query'; import { find, keys, get } from 'lodash'; +import { esFilters } from '../../../../../common/es_query'; -export const mapDefault = (filter: Filter) => { +export const mapDefault = (filter: esFilters.Filter) => { const metaProperty = /(^\$|meta)/; const key = find(keys(filter), item => !item.match(metaProperty)); if (key) { - const type = FILTERS.CUSTOM; + const type = esFilters.FILTERS.CUSTOM; const value = JSON.stringify(get(filter, key, {})); return { type, key, value }; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts index c352d3e2b9a734..ff0ed4f4e4d94a 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts @@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import { ExistsFilter, buildEmptyFilter, buildExistsFilter } from '@kbn/es-query'; + import { mapExists } from './map_exists'; import { mapQueryString } from './map_query_string'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapExists()', () => { test('should return the key and value for matching filters', async () => { - const filter: ExistsFilter = buildExistsFilter({ name: '_type' }, 'index'); + const filter = esFilters.buildExistsFilter({ name: '_type' }, 'index'); const result = mapExists(filter); expect(result).toHaveProperty('key', '_type'); @@ -31,7 +32,7 @@ describe('filter manager utilities', () => { }); test('should return undefined for none matching', async done => { - const filter = buildEmptyFilter(true) as ExistsFilter; + const filter = esFilters.buildEmptyFilter(true); try { mapQueryString(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts index d539219a1ca24e..63665bdd88ccbe 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts @@ -17,14 +17,14 @@ * under the License. */ -import { Filter, isExistsFilter, FILTERS } from '@kbn/es-query'; import { get } from 'lodash'; +import { esFilters } from '../../../../../common/es_query'; -export const mapExists = (filter: Filter) => { - if (isExistsFilter(filter)) { +export const mapExists = (filter: esFilters.Filter) => { + if (esFilters.isExistsFilter(filter)) { return { - type: FILTERS.EXISTS, - value: FILTERS.EXISTS, + type: esFilters.FILTERS.EXISTS, + value: esFilters.FILTERS.EXISTS, key: get(filter, 'exists.field'), }; } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts index c3c99e6f6c4a37..5fca4a652bad88 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts @@ -18,7 +18,7 @@ */ import { mapGeoBoundingBox } from './map_geo_bounding_box'; -import { Filter, GeoBoundingBoxFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapGeoBoundingBox()', () => { @@ -34,7 +34,7 @@ describe('filter manager utilities', () => { bottom_right: { lat: 15, lon: 20 }, }, }, - } as GeoBoundingBoxFilter; + } as esFilters.GeoBoundingBoxFilter; const result = mapGeoBoundingBox(filter); @@ -63,7 +63,8 @@ describe('filter manager utilities', () => { bottom_right: { lat: 15, lon: 20 }, }, }, - } as GeoBoundingBoxFilter; + } as esFilters.GeoBoundingBoxFilter; + const result = mapGeoBoundingBox(filter); expect(result).toHaveProperty('key', 'point'); @@ -82,7 +83,7 @@ describe('filter manager utilities', () => { const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } }, - } as Filter; + } as esFilters.Filter; try { mapGeoBoundingBox(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts index 1f9b8cd842509b..091e9a3f34000a 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts @@ -16,16 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { - GeoBoundingBoxFilter, - Filter, - FILTERS, - isGeoBoundingBoxFilter, - FilterValueFormatter, -} from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; const getFormattedValueFn = (params: any) => { - return (formatter?: FilterValueFormatter) => { + return (formatter?: esFilters.FilterValueFormatter) => { const corners = formatter ? { topLeft: formatter.convert(params.top_left), @@ -40,20 +34,20 @@ const getFormattedValueFn = (params: any) => { }; }; -const getParams = (filter: GeoBoundingBoxFilter) => { +const getParams = (filter: esFilters.GeoBoundingBoxFilter) => { const key = Object.keys(filter.geo_bounding_box).filter(k => k !== 'ignore_unmapped')[0]; const params = filter.geo_bounding_box[key]; return { key, params, - type: FILTERS.GEO_BOUNDING_BOX, + type: esFilters.FILTERS.GEO_BOUNDING_BOX, value: getFormattedValueFn(params), }; }; -export const mapGeoBoundingBox = (filter: Filter) => { - if (!isGeoBoundingBoxFilter(filter)) { +export const mapGeoBoundingBox = (filter: esFilters.Filter) => { + if (!esFilters.isGeoBoundingBoxFilter(filter)) { throw filter; } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts index ee4f9b295d682d..3afa3891a24bb8 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts @@ -16,23 +16,28 @@ * specific language governing permissions and limitations * under the License. */ + import { mapGeoPolygon } from './map_geo_polygon'; -import { GeoPolygonFilter, Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { - describe('mapGeoPolygon()', () => { - test('should return the key and value for matching filters with bounds', async () => { - const filter = { - meta: { - index: 'logstash-*', - }, - geo_polygon: { - point: { - points: [{ lat: 5, lon: 10 }, { lat: 15, lon: 20 }], - }, + let filter: esFilters.GeoPolygonFilter; + + beforeEach(() => { + filter = { + meta: { + index: 'logstash-*', + }, + geo_polygon: { + point: { + points: [{ lat: 5, lon: 10 }, { lat: 15, lon: 20 }], }, - } as GeoPolygonFilter; + }, + } as esFilters.GeoPolygonFilter; + }); + describe('mapGeoPolygon()', () => { + test('should return the key and value for matching filters with bounds', async () => { const result = mapGeoPolygon(filter); expect(result).toHaveProperty('key', 'point'); @@ -48,17 +53,6 @@ describe('filter manager utilities', () => { }); test('should return the key and value even when using ignore_unmapped', async () => { - const filter = { - meta: { - index: 'logstash-*', - }, - geo_polygon: { - ignore_unmapped: true, - point: { - points: [{ lat: 5, lon: 10 }, { lat: 15, lon: 20 }], - }, - }, - } as GeoPolygonFilter; const result = mapGeoPolygon(filter); expect(result).toHaveProperty('key', 'point'); @@ -74,15 +68,15 @@ describe('filter manager utilities', () => { }); test('should return undefined for none matching', async done => { - const filter = { + const wrongFilter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } }, - } as Filter; + } as esFilters.Filter; try { - mapGeoPolygon(filter); + mapGeoPolygon(wrongFilter); } catch (e) { - expect(e).toBe(filter); + expect(e).toBe(wrongFilter); done(); } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts index 03ce4130d0c972..a7881b4a145a19 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts @@ -16,38 +16,33 @@ * specific language governing permissions and limitations * under the License. */ -import { - GeoPolygonFilter, - Filter, - FILTERS, - isGeoPolygonFilter, - FilterValueFormatter, -} from '@kbn/es-query'; + +import { esFilters } from '../../../../../common/es_query'; const POINTS_SEPARATOR = ', '; const getFormattedValueFn = (points: string[]) => { - return (formatter?: FilterValueFormatter) => { + return (formatter?: esFilters.FilterValueFormatter) => { return points .map((point: string) => (formatter ? formatter.convert(point) : JSON.stringify(point))) .join(POINTS_SEPARATOR); }; }; -function getParams(filter: GeoPolygonFilter) { +function getParams(filter: esFilters.GeoPolygonFilter) { const key = Object.keys(filter.geo_polygon).filter(k => k !== 'ignore_unmapped')[0]; const params = filter.geo_polygon[key]; return { key, params, - type: FILTERS.GEO_POLYGON, + type: esFilters.FILTERS.GEO_POLYGON, value: getFormattedValueFn(params.points || []), }; } -export function mapGeoPolygon(filter: Filter) { - if (!isGeoPolygonFilter(filter)) { +export function mapGeoPolygon(filter: esFilters.Filter) { + if (!esFilters.isGeoPolygonFilter(filter)) { throw filter; } return getParams(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts index 2f0641598a2ce3..4fc6d0b4924141 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { MatchAllFilter } from '@kbn/es-query'; + import { mapMatchAll } from './map_match_all'; +import { esFilters } from '../../../../../common/es_query'; describe('filter_manager/lib', () => { describe('mapMatchAll()', () => { - let filter: MatchAllFilter; + let filter: esFilters.MatchAllFilter; beforeEach(() => { filter = { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts index a1387e6dbe4574..4e93b1d41e9a82 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter, FILTERS, isMatchAllFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; -export const mapMatchAll = (filter: Filter) => { - if (isMatchAllFilter(filter)) { +export const mapMatchAll = (filter: esFilters.Filter) => { + if (esFilters.isMatchAllFilter(filter)) { return { - type: FILTERS.MATCH_ALL, + type: esFilters.FILTERS.MATCH_ALL, key: filter.meta.field, value: filter.meta.formattedValue || 'all', }; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts index ca23f25826906b..1847eb37ca42ff 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts @@ -16,15 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import { MissingFilter, buildEmptyFilter, ExistsFilter } from '@kbn/es-query'; + import { mapMissing } from './map_missing'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapMissing()', () => { test('should return the key and value for matching filters', async () => { - const filter: MissingFilter = { + const filter: esFilters.MissingFilter = { missing: { field: '_type' }, - ...buildEmptyFilter(true), + ...esFilters.buildEmptyFilter(true), }; const result = mapMissing(filter); @@ -33,7 +34,7 @@ describe('filter manager utilities', () => { }); test('should return undefined for none matching', async done => { - const filter = buildEmptyFilter(true) as ExistsFilter; + const filter = esFilters.buildEmptyFilter(true); try { mapMissing(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts index 861a84ed616468..51dee89ad884b2 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts @@ -16,13 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter, FILTERS, isMissingFilter } from '@kbn/es-query'; -export const mapMissing = (filter: Filter) => { - if (isMissingFilter(filter)) { +import { esFilters } from '../../../../../common/es_query'; + +export const mapMissing = (filter: esFilters.Filter) => { + if (esFilters.isMissingFilter(filter)) { return { - type: FILTERS.MISSING, - value: FILTERS.MISSING, + type: esFilters.FILTERS.MISSING, + value: esFilters.FILTERS.MISSING, key: filter.missing.field, }; } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts index c95a2529add149..05372d37264b06 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts @@ -17,7 +17,7 @@ * under the License. */ import { mapPhrase } from './map_phrase'; -import { PhraseFilter, Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapPhrase()', () => { @@ -25,11 +25,13 @@ describe('filter manager utilities', () => { const filter = { meta: { index: 'logstash-*' }, query: { match: { _type: { query: 'apache', type: 'phrase' } } }, - } as PhraseFilter; + } as esFilters.PhraseFilter; + const result = mapPhrase(filter); expect(result).toHaveProperty('value'); expect(result).toHaveProperty('key', '_type'); + if (result.value) { const displayName = result.value(); expect(displayName).toBe('apache'); @@ -40,7 +42,7 @@ describe('filter manager utilities', () => { const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } }, - } as Filter; + } as esFilters.Filter; try { mapPhrase(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts index efa348c9ad3206..b6e9c2007db970 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts @@ -18,45 +18,36 @@ */ import { get } from 'lodash'; -import { - PhraseFilter, - Filter, - FILTERS, - isPhraseFilter, - isScriptedPhraseFilter, - getPhraseFilterField, - getPhraseFilterValue, - FilterValueFormatter, -} from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; -const getScriptedPhraseValue = (filter: PhraseFilter) => +const getScriptedPhraseValue = (filter: esFilters.PhraseFilter) => get(filter, ['script', 'script', 'params', 'value']); const getFormattedValueFn = (value: any) => { - return (formatter?: FilterValueFormatter) => { + return (formatter?: esFilters.FilterValueFormatter) => { return formatter ? formatter.convert(value) : value; }; }; -const getParams = (filter: PhraseFilter) => { +const getParams = (filter: esFilters.PhraseFilter) => { const scriptedPhraseValue = getScriptedPhraseValue(filter); const isScriptedFilter = Boolean(scriptedPhraseValue); - const key = isScriptedFilter ? filter.meta.field || '' : getPhraseFilterField(filter); - const query = scriptedPhraseValue || getPhraseFilterValue(filter); + const key = isScriptedFilter ? filter.meta.field || '' : esFilters.getPhraseFilterField(filter); + const query = scriptedPhraseValue || esFilters.getPhraseFilterValue(filter); const params = { query }; return { key, params, - type: FILTERS.PHRASE, + type: esFilters.FILTERS.PHRASE, value: getFormattedValueFn(query), }; }; -export const isMapPhraseFilter = (filter: any): filter is PhraseFilter => - isPhraseFilter(filter) || isScriptedPhraseFilter(filter); +export const isMapPhraseFilter = (filter: any): filter is esFilters.PhraseFilter => + esFilters.isPhraseFilter(filter) || esFilters.isScriptedPhraseFilter(filter); -export const mapPhrase = (filter: Filter) => { +export const mapPhrase = (filter: esFilters.Filter) => { if (!isMapPhraseFilter(filter)) { throw filter; } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts index c17ff11d49fd4a..7240d87d02b5ab 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts @@ -17,10 +17,10 @@ * under the License. */ -import { Filter, isPhrasesFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; -export const mapPhrases = (filter: Filter) => { - if (!isPhrasesFilter(filter)) { +export const mapPhrases = (filter: esFilters.Filter) => { + if (!esFilters.isPhrasesFilter(filter)) { throw filter; } diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts index 4b1a5d39c405da..c60e7d3454fe0f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts @@ -17,27 +17,28 @@ * under the License. */ -import { QueryStringFilter, buildQueryFilter, buildEmptyFilter } from '@kbn/es-query'; import { mapQueryString } from './map_query_string'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapQueryString()', () => { test('should return the key and value for matching filters', async () => { - const filter: QueryStringFilter = buildQueryFilter( + const filter = esFilters.buildQueryFilter( { query_string: { query: 'foo:bar' } }, - 'index' + 'index', + '' ); - const result = mapQueryString(filter); + const result = mapQueryString(filter as esFilters.Filter); expect(result).toHaveProperty('key', 'query'); expect(result).toHaveProperty('value', 'foo:bar'); }); test('should return undefined for none matching', async done => { - const filter = buildEmptyFilter(true) as QueryStringFilter; + const filter = esFilters.buildEmptyFilter(true); try { - mapQueryString(filter); + mapQueryString(filter as esFilters.Filter); } catch (e) { expect(e).toBe(filter); done(); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts index 94da8074edd04d..20c3555639a3ed 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter, FILTERS, isQueryStringFilter } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; -export const mapQueryString = (filter: Filter) => { - if (isQueryStringFilter(filter)) { +export const mapQueryString = (filter: esFilters.Filter) => { + if (esFilters.isQueryStringFilter(filter)) { return { - type: FILTERS.QUERY_STRING, + type: esFilters.FILTERS.QUERY_STRING, key: 'query', value: filter.query.query_string.query, }; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts index 12d2919e2d47b6..c0d5773d6f2c14 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts @@ -18,30 +18,15 @@ */ import { mapRange } from './map_range'; -import { RangeFilter, Filter, FilterMeta } from '@kbn/es-query'; +import { esFilters } from '../../../../../common/es_query'; describe('filter manager utilities', () => { describe('mapRange()', () => { test('should return the key and value for matching filters with gt/lt', async () => { const filter = { - meta: { index: 'logstash-*' } as FilterMeta, + meta: { index: 'logstash-*' } as esFilters.FilterMeta, range: { bytes: { lt: 2048, gt: 1024 } }, - } as RangeFilter; - const result = mapRange(filter); - - expect(result).toHaveProperty('key', 'bytes'); - expect(result).toHaveProperty('value'); - if (result.value) { - const displayName = result.value(); - expect(displayName).toBe('1024 to 2048'); - } - }); - - test('should return the key and value for matching filters with gte/lte', async () => { - const filter = { - meta: { index: 'logstash-*' } as FilterMeta, - range: { bytes: { lte: 2048, gte: 1024 } }, - } as RangeFilter; + } as esFilters.RangeFilter; const result = mapRange(filter); expect(result).toHaveProperty('key', 'bytes'); @@ -56,7 +41,7 @@ describe('filter manager utilities', () => { const filter = { meta: { index: 'logstash-*' }, query: { query_string: { query: 'foo:bar' } }, - } as Filter; + } as esFilters.Filter; try { mapRange(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts index 76f9d3621e1717..51fb970f5f03ea 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts @@ -17,18 +17,11 @@ * under the License. */ -import { - Filter, - RangeFilter, - FILTERS, - isRangeFilter, - isScriptedRangeFilter, - FilterValueFormatter, -} from '@kbn/es-query'; import { get, has } from 'lodash'; +import { esFilters } from '../../../../../common/es_query'; const getFormattedValueFn = (left: any, right: any) => { - return (formatter?: FilterValueFormatter) => { + return (formatter?: esFilters.FilterValueFormatter) => { let displayValue = `${left} to ${right}`; if (formatter) { const convert = formatter.getConverterFor('text'); @@ -38,11 +31,12 @@ const getFormattedValueFn = (left: any, right: any) => { }; }; -const getFirstRangeKey = (filter: RangeFilter) => filter.range && Object.keys(filter.range)[0]; -const getRangeByKey = (filter: RangeFilter, key: string) => get(filter, ['range', key]); +const getFirstRangeKey = (filter: esFilters.RangeFilter) => + filter.range && Object.keys(filter.range)[0]; +const getRangeByKey = (filter: esFilters.RangeFilter, key: string) => get(filter, ['range', key]); -function getParams(filter: RangeFilter) { - const isScriptedRange = isScriptedRangeFilter(filter); +function getParams(filter: esFilters.RangeFilter) { + const isScriptedRange = esFilters.isScriptedRangeFilter(filter); const key: string = (isScriptedRange ? filter.meta.field : getFirstRangeKey(filter)) || ''; const params: any = isScriptedRange ? get(filter, 'script.script.params') @@ -56,13 +50,13 @@ function getParams(filter: RangeFilter) { const value = getFormattedValueFn(left, right); - return { type: FILTERS.RANGE, key, value, params }; + return { type: esFilters.FILTERS.RANGE, key, value, params }; } -export const isMapRangeFilter = (filter: any): filter is RangeFilter => - isRangeFilter(filter) || isScriptedRangeFilter(filter); +export const isMapRangeFilter = (filter: any): filter is esFilters.RangeFilter => + esFilters.isRangeFilter(filter) || esFilters.isScriptedRangeFilter(filter); -export const mapRange = (filter: Filter) => { +export const mapRange = (filter: esFilters.Filter) => { if (!isMapRangeFilter(filter)) { throw filter; } diff --git a/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts index 3fedcf97a625ac..b9731797c9ee36 100644 --- a/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts @@ -17,8 +17,8 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { onlyDisabledFiltersChanged } from './only_disabled'; +import { esFilters } from '../../../../../data/public'; describe('filter manager utilities', () => { describe('onlyDisabledFiltersChanged()', () => { @@ -27,20 +27,20 @@ describe('filter manager utilities', () => { { meta: { disabled: true } }, { meta: { disabled: true } }, { meta: { disabled: true } }, - ] as Filter[]; - const newFilters = [{ meta: { disabled: true } }] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [{ meta: { disabled: true } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(true); }); test('should return false if there are no old filters', () => { - const newFilters = [{ meta: { disabled: false } }] as Filter[]; + const newFilters = [{ meta: { disabled: false } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, undefined)).toBe(false); }); test('should return false if there are no new filters', () => { - const filters = [{ meta: { disabled: false } }] as Filter[]; + const filters = [{ meta: { disabled: false } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(undefined, filters)).toBe(false); }); @@ -50,8 +50,8 @@ describe('filter manager utilities', () => { { meta: { disabled: false } }, { meta: { disabled: false } }, { meta: { disabled: false } }, - ] as Filter[]; - const newFilters = [{ meta: { disabled: false } }] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [{ meta: { disabled: false } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); @@ -61,8 +61,8 @@ describe('filter manager utilities', () => { { meta: { disabled: true } }, { meta: { disabled: true } }, { meta: { disabled: true } }, - ] as Filter[]; - const newFilters = [{ meta: { disabled: false } }] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [{ meta: { disabled: false } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); @@ -72,8 +72,8 @@ describe('filter manager utilities', () => { { meta: { disabled: false } }, { meta: { disabled: false } }, { meta: { disabled: false } }, - ] as Filter[]; - const newFilters = [{ meta: { disabled: true } }] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [{ meta: { disabled: true } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); @@ -83,8 +83,8 @@ describe('filter manager utilities', () => { { meta: { disabled: true } }, { meta: { disabled: true } }, { meta: { disabled: true } }, - ] as Filter[]; - const newFilters = [] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(true); }); @@ -94,8 +94,8 @@ describe('filter manager utilities', () => { { meta: { disabled: false } }, { meta: { disabled: false } }, { meta: { disabled: false } }, - ] as Filter[]; - const newFilters = [] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); @@ -104,11 +104,11 @@ describe('filter manager utilities', () => { const filters = [ { meta: { disabled: true, negate: false } }, { meta: { disabled: true, negate: false } }, - ] as Filter[]; + ] as esFilters.Filter[]; const newFilters = [ { meta: { disabled: true, negate: true } }, { meta: { disabled: true, negate: true } }, - ] as Filter[]; + ] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(true); }); @@ -118,8 +118,8 @@ describe('filter manager utilities', () => { { meta: { disabled: false } }, { meta: { disabled: false } }, { meta: { disabled: true } }, - ] as Filter[]; - const newFilters = [{ meta: { disabled: false } }] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [{ meta: { disabled: false } }] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); @@ -129,15 +129,15 @@ describe('filter manager utilities', () => { { meta: { disabled: true } }, { meta: { disabled: false } }, { meta: { disabled: true } }, - ] as Filter[]; - const newFilters = [] as Filter[]; + ] as esFilters.Filter[]; + const newFilters = [] as esFilters.Filter[]; expect(onlyDisabledFiltersChanged(newFilters, filters)).toBe(false); }); test('should not throw with null filters', () => { - const filters = [null, { meta: { disabled: true } }] as Filter[]; - const newFilters = [] as Filter[]; + const filters = [null, { meta: { disabled: true } }] as esFilters.Filter[]; + const newFilters = [] as esFilters.Filter[]; expect(() => { onlyDisabledFiltersChanged(newFilters, filters); diff --git a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts index 9c0b5f43acb3ef..0fb6894a297a1f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts +++ b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts @@ -17,17 +17,20 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { filter, isEqual } from 'lodash'; +import { esFilters } from '../../../../../../plugins/data/public'; -const isEnabled = (f: Filter) => f && f.meta && !f.meta.disabled; +const isEnabled = (f: esFilters.Filter) => f && f.meta && !f.meta.disabled; /** * Checks to see if only disabled filters have been changed * * @returns {bool} Only disabled filters */ -export const onlyDisabledFiltersChanged = (newFilters?: Filter[], oldFilters?: Filter[]) => { +export const onlyDisabledFiltersChanged = ( + newFilters?: esFilters.Filter[], + oldFilters?: esFilters.Filter[] +) => { // If it's the same - compare only enabled filters const newEnabledFilters = filter(newFilters || [], isEnabled); const oldEnabledFilters = filter(oldFilters || [], isEnabled); diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts index 86f059913cd96a..08eeabc1497e3c 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts @@ -16,15 +16,24 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter, buildQueryFilter, FilterStateStore } from '@kbn/es-query'; + import { uniqFilters } from './uniq_filters'; +import { esFilters } from '../../../../../data/public'; describe('filter manager utilities', () => { describe('niqFilter', () => { test('should filter out dups', () => { - const before: Filter[] = [ - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), + const before: esFilters.Filter[] = [ + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), ]; const results = uniqFilters(before); @@ -32,9 +41,17 @@ describe('filter manager utilities', () => { }); test('should filter out duplicates, ignoring meta attributes', () => { - const before: Filter[] = [ - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index1'), - buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index2'), + const before: esFilters.Filter[] = [ + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index1', + '' + ), + esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index2', + '' + ), ]; const results = uniqFilters(before); @@ -42,14 +59,22 @@ describe('filter manager utilities', () => { }); test('should filter out duplicates, ignoring $state attributes', () => { - const before: Filter[] = [ + const before: esFilters.Filter[] = [ { - $state: { store: FilterStateStore.APP_STATE }, - ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), + $state: { store: esFilters.FilterStateStore.APP_STATE }, + ...esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), }, { - $state: { store: FilterStateStore.GLOBAL_STATE }, - ...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index'), + $state: { store: esFilters.FilterStateStore.GLOBAL_STATE }, + ...esFilters.buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ), }, ]; const results = uniqFilters(before); diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts index 12e793253371e7..e96c52e6db3dec 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter } from '@kbn/es-query'; import { each, union } from 'lodash'; import { dedupFilters } from './dedup_filters'; +import { esFilters } from '../../../../../data/public'; /** * Remove duplicate filters from an array of filters @@ -28,10 +28,10 @@ import { dedupFilters } from './dedup_filters'; * @returns {object} The original filters array with duplicates removed */ -export const uniqFilters = (filters: Filter[], comparatorOptions: any = {}) => { - let results: Filter[] = []; +export const uniqFilters = (filters: esFilters.Filter[], comparatorOptions: any = {}) => { + let results: esFilters.Filter[] = []; - each(filters, (filter: Filter) => { + each(filters, (filter: esFilters.Filter) => { results = union(results, dedupFilters(results, [filter]), comparatorOptions); }); diff --git a/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts index 27f627b477c359..aa047647c57516 100644 --- a/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts +++ b/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts @@ -17,9 +17,9 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; -export function getFiltersArray(): Filter[] { +export function getFiltersArray(): esFilters.Filter[] { return [ { query: { match: { extension: { query: 'jpg', type: 'phrase' } } }, diff --git a/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts index 20d9e236f49be8..adc72c961b08bb 100644 --- a/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts +++ b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts @@ -17,15 +17,15 @@ * under the License. */ -import { Filter, FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../../../plugins/data/public'; export function getFilter( - store: FilterStateStore, + store: esFilters.FilterStateStore, disabled: boolean, negated: boolean, queryKey: string, queryValue: any -): Filter { +): esFilters.Filter { return { $state: { store, diff --git a/src/plugins/data/public/query/filter_manager/types.ts b/src/plugins/data/public/query/filter_manager/types.ts index e74b48b722cc45..0b3dbca2d6e0a6 100644 --- a/src/plugins/data/public/query/filter_manager/types.ts +++ b/src/plugins/data/public/query/filter_manager/types.ts @@ -17,9 +17,9 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../plugins/data/public'; export interface PartitionedFilters { - globalFilters: Filter[]; - appFilters: Filter[]; + globalFilters: esFilters.Filter[]; + appFilters: esFilters.Filter[]; } diff --git a/src/plugins/data/public/query/index.tsx b/src/plugins/data/public/query/index.tsx index 44b371b6adf192..42647b9d98201d 100644 --- a/src/plugins/data/public/query/index.tsx +++ b/src/plugins/data/public/query/index.tsx @@ -18,5 +18,4 @@ */ export * from './query_service'; - export * from './filter_manager'; diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index df933167cee255..f0b6117b928cd8 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -25,6 +25,11 @@ export function plugin(initializerContext: PluginInitializerContext) { } export { DataServerPlugin as Plugin }; +export { + IndexPatternsFetcher, + FieldDescriptor, + shouldReadFieldFromDocValues, +} from './index_patterns'; export * from './search'; diff --git a/src/plugins/data/server/index_patterns/fetcher/index.ts b/src/plugins/data/server/index_patterns/fetcher/index.ts new file mode 100644 index 00000000000000..19306696885dbe --- /dev/null +++ b/src/plugins/data/server/index_patterns/fetcher/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 * from './index_patterns_fetcher'; +export { shouldReadFieldFromDocValues } from './lib'; diff --git a/src/legacy/server/index_patterns/service/index_patterns_service.ts b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts similarity index 98% rename from src/legacy/server/index_patterns/service/index_patterns_service.ts rename to src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts index 62cf51828a4cc2..5f1493a49ab7de 100644 --- a/src/legacy/server/index_patterns/service/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/fetcher/index_patterns_fetcher.ts @@ -36,7 +36,7 @@ interface FieldSubType { nested?: { path: string }; } -export class IndexPatternsService { +export class IndexPatternsFetcher { private _callDataCluster: APICaller; constructor(callDataCluster: APICaller) { diff --git a/src/legacy/server/index_patterns/service/lib/errors.ts b/src/plugins/data/server/index_patterns/fetcher/lib/errors.ts similarity index 100% rename from src/legacy/server/index_patterns/service/lib/errors.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/errors.ts diff --git a/src/legacy/server/index_patterns/service/lib/es_api.test.js b/src/plugins/data/server/index_patterns/fetcher/lib/es_api.test.js similarity index 100% rename from src/legacy/server/index_patterns/service/lib/es_api.test.js rename to src/plugins/data/server/index_patterns/fetcher/lib/es_api.test.js diff --git a/src/legacy/server/index_patterns/service/lib/es_api.ts b/src/plugins/data/server/index_patterns/fetcher/lib/es_api.ts similarity index 99% rename from src/legacy/server/index_patterns/service/lib/es_api.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/es_api.ts index 63c1824784dbdd..92b64fafddd665 100644 --- a/src/legacy/server/index_patterns/service/lib/es_api.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/es_api.ts @@ -18,7 +18,6 @@ */ import { APICaller } from 'src/core/server'; -// @ts-ignore import { convertEsError } from './errors'; import { FieldCapsResponse } from './field_capabilities'; diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/__fixtures__/es_field_caps_response.json b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/__fixtures__/es_field_caps_response.json similarity index 100% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/__fixtures__/es_field_caps_response.json rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/__fixtures__/es_field_caps_response.json diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_capabilities.test.js b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.test.js similarity index 100% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/field_capabilities.test.js rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.test.js diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_capabilities.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts similarity index 97% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/field_capabilities.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts index 05e8d591f13a9e..c275fb714088e9 100644 --- a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_capabilities.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts @@ -23,7 +23,7 @@ import { APICaller } from 'src/core/server'; import { callFieldCapsApi } from '../es_api'; import { FieldCapsResponse, readFieldCapsResponse } from './field_caps_response'; import { mergeOverrides } from './overrides'; -import { FieldDescriptor } from '../../index_patterns_service'; +import { FieldDescriptor } from '../../index_patterns_fetcher'; export function concatIfUniq(arr: T[], value: T) { return arr.includes(value) ? arr : arr.concat(value); diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.test.js b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js similarity index 99% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.test.js rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js index c0a5492aaba360..cf4af615b95779 100644 --- a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.test.js +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js @@ -24,7 +24,7 @@ import sinon from 'sinon'; import * as shouldReadFieldFromDocValuesNS from './should_read_field_from_doc_values'; import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; -import { getKbnFieldType } from '../../../../../../plugins/data/common'; +import { getKbnFieldType } from '../../../../../../data/common'; import { readFieldCapsResponse } from './field_caps_response'; import esResponse from './__fixtures__/es_field_caps_response.json'; diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts similarity index 98% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts index 19288014de297a..06eb30db0b24bb 100644 --- a/src/legacy/server/index_patterns/service/lib/field_capabilities/field_caps_response.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts @@ -18,9 +18,9 @@ */ import { uniq } from 'lodash'; -import { FieldDescriptor } from '../..'; +import { castEsToKbnFieldTypeName } from '../../../../../common'; import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; -import { castEsToKbnFieldTypeName } from '../../../../../../plugins/data/common'; +import { FieldDescriptor } from '../../../fetcher'; interface FieldCapObject { type: string; diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/index.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/index.ts similarity index 91% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/index.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/index.ts index 8ac42255577841..6a541aff564714 100644 --- a/src/legacy/server/index_patterns/service/lib/field_capabilities/index.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/index.ts @@ -19,3 +19,4 @@ export { getFieldCapabilities } from './field_capabilities'; export { FieldCapsResponse } from './field_caps_response'; +export { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/overrides.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/overrides.ts similarity index 95% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/overrides.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/overrides.ts index 6310bf02e4430d..518bfeccac01ab 100644 --- a/src/legacy/server/index_patterns/service/lib/field_capabilities/overrides.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/overrides.ts @@ -18,7 +18,7 @@ */ import { merge } from 'lodash'; -import { FieldDescriptor } from '../../index_patterns_service'; +import { FieldDescriptor } from '../../index_patterns_fetcher'; const OVERRIDES: Record> = { _source: { type: '_source' }, diff --git a/src/legacy/server/index_patterns/service/lib/field_capabilities/should_read_field_from_doc_values.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts similarity index 100% rename from src/legacy/server/index_patterns/service/lib/field_capabilities/should_read_field_from_doc_values.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/should_read_field_from_doc_values.ts diff --git a/src/legacy/server/index_patterns/service/lib/index.ts b/src/plugins/data/server/index_patterns/fetcher/lib/index.ts similarity index 90% rename from src/legacy/server/index_patterns/service/lib/index.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/index.ts index cd239b3753aa31..20e74d2b1a579d 100644 --- a/src/legacy/server/index_patterns/service/lib/index.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/index.ts @@ -17,8 +17,6 @@ * under the License. */ -export { getFieldCapabilities } from './field_capabilities'; -// @ts-ignore +export { getFieldCapabilities, shouldReadFieldFromDocValues } from './field_capabilities'; export { resolveTimePattern } from './resolve_time_pattern'; -// @ts-ignore export { createNoMatchingIndicesError } from './errors'; diff --git a/src/legacy/server/index_patterns/service/lib/resolve_time_pattern.test.js b/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.test.js similarity index 100% rename from src/legacy/server/index_patterns/service/lib/resolve_time_pattern.test.js rename to src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.test.js diff --git a/src/legacy/server/index_patterns/service/lib/resolve_time_pattern.ts b/src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts similarity index 100% rename from src/legacy/server/index_patterns/service/lib/resolve_time_pattern.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/resolve_time_pattern.ts diff --git a/src/legacy/server/index_patterns/service/lib/time_pattern_to_wildcard.test.ts b/src/plugins/data/server/index_patterns/fetcher/lib/time_pattern_to_wildcard.test.ts similarity index 100% rename from src/legacy/server/index_patterns/service/lib/time_pattern_to_wildcard.test.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/time_pattern_to_wildcard.test.ts diff --git a/src/legacy/server/index_patterns/service/lib/time_pattern_to_wildcard.ts b/src/plugins/data/server/index_patterns/fetcher/lib/time_pattern_to_wildcard.ts similarity index 100% rename from src/legacy/server/index_patterns/service/lib/time_pattern_to_wildcard.ts rename to src/plugins/data/server/index_patterns/fetcher/lib/time_pattern_to_wildcard.ts diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.mocks.ts b/src/plugins/data/server/index_patterns/index.ts similarity index 83% rename from src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.mocks.ts rename to src/plugins/data/server/index_patterns/index.ts index 04c99a1547ba93..6937fa22c4e5d1 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/wizard/new_vis_modal.test.mocks.ts +++ b/src/plugins/data/server/index_patterns/index.ts @@ -17,10 +17,5 @@ * under the License. */ -export const settingsGet = jest.fn(); - -jest.doMock('ui/chrome', () => ({ - getUiSettingsClient: () => ({ - get: settingsGet, - }), -})); +export { IndexPatternsFetcher, FieldDescriptor, shouldReadFieldFromDocValues } from './fetcher'; +export { IndexPatternsService } from './index_patterns_service'; diff --git a/packages/kbn-es-query/src/filters/query.js b/src/plugins/data/server/index_patterns/index_patterns_service.ts similarity index 72% rename from packages/kbn-es-query/src/filters/query.js rename to src/plugins/data/server/index_patterns/index_patterns_service.ts index ded877231bf67e..30d199c0e522e4 100644 --- a/packages/kbn-es-query/src/filters/query.js +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -17,18 +17,14 @@ * under the License. */ -// Creates a filter corresponding to a raw Elasticsearch query DSL object -export function buildQueryFilter(query, index, alias) { - const filter = { - query: query, - meta: { - index, - } - }; +import { CoreSetup } from 'kibana/server'; +import { Plugin } from '../../../../core/server'; +import { registerRoutes } from './routes'; - if (alias) { - filter.meta.alias = alias; +export class IndexPatternsService implements Plugin { + public setup({ http, elasticsearch }: CoreSetup) { + registerRoutes(http, elasticsearch); } - return filter; + public start() {} } diff --git a/src/legacy/server/index_patterns/routes.ts b/src/plugins/data/server/index_patterns/routes.ts similarity index 90% rename from src/legacy/server/index_patterns/routes.ts rename to src/plugins/data/server/index_patterns/routes.ts index fb78b94e0f77f4..7975e923e219bd 100644 --- a/src/legacy/server/index_patterns/routes.ts +++ b/src/plugins/data/server/index_patterns/routes.ts @@ -25,18 +25,18 @@ import { RequestHandlerContext, APICaller, CallAPIOptions, -} from '../../../core/server'; -import { IndexPatternsService } from './service'; +} from '../../../../core/server'; +import { IndexPatternsFetcher } from './fetcher'; -export function registerRoutes(core: CoreSetup) { - const getIndexPatternsService = async (request: KibanaRequest): Promise => { - const client = await core.elasticsearch.dataClient$.pipe(first()).toPromise(); +export function registerRoutes(http: CoreSetup['http'], elasticsearch: CoreSetup['elasticsearch']) { + const getIndexPatternsService = async (request: KibanaRequest): Promise => { + const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); const callCluster: APICaller = ( endpoint: string, params?: Record, options?: CallAPIOptions ) => client.asScoped(request).callAsCurrentUser(endpoint, params, options); - return new Promise(resolve => resolve(new IndexPatternsService(callCluster))); + return new Promise(resolve => resolve(new IndexPatternsFetcher(callCluster))); }; const parseMetaFields = (metaFields: string | string[]) => { @@ -49,7 +49,7 @@ export function registerRoutes(core: CoreSetup) { return parsedFields; }; - const router = core.http.createRouter(); + const router = http.createRouter(); router.get( { path: '/api/index_patterns/_fields_for_wildcard', diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 9cf08b0702e9e0..e81250e653ebd3 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -18,19 +18,21 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/server'; +import { IndexPatternsService } from './index_patterns'; import { ISearchSetup } from './search'; import { SearchService } from './search/search_service'; export interface DataPluginSetup { search: ISearchSetup; } - export class DataServerPlugin implements Plugin { private readonly searchService: SearchService; + private readonly indexPatterns = new IndexPatternsService(); constructor(initializerContext: PluginInitializerContext) { this.searchService = new SearchService(initializerContext); } public setup(core: CoreSetup) { + this.indexPatterns.setup(core); return { search: this.searchService.setup(core), }; diff --git a/src/plugins/embeddable/public/_index.scss b/src/plugins/embeddable/public/_index.scss new file mode 100644 index 00000000000000..ed80b3f9983e52 --- /dev/null +++ b/src/plugins/embeddable/public/_index.scss @@ -0,0 +1,3 @@ +@import './variables'; +@import './lib/panel/index'; +@import './lib/panel/panel_header/index'; diff --git a/src/legacy/core_plugins/embeddable_api/public/_variables.scss b/src/plugins/embeddable/public/_variables.scss similarity index 100% rename from src/legacy/core_plugins/embeddable_api/public/_variables.scss rename to src/plugins/embeddable/public/_variables.scss diff --git a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts index 99cfb2ea13d07b..e2592b70397f33 100644 --- a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts +++ b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts @@ -18,16 +18,16 @@ */ import { i18n } from '@kbn/i18n'; -import { Filter } from '@kbn/es-query'; import { IAction, createAction, IncompatibleActionError } from '../ui_actions'; import { IEmbeddable, EmbeddableInput } from '../embeddables'; +import { esFilters } from '../../../../../plugins/data/public'; export const APPLY_FILTER_ACTION = 'APPLY_FILTER_ACTION'; -type RootEmbeddable = IEmbeddable; +type RootEmbeddable = IEmbeddable; interface ActionContext { embeddable: IEmbeddable; - filters: Filter[]; + filters: esFilters.Filter[]; } async function isCompatible(context: ActionContext) { diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx index 802be5bf1282ec..47113ffc59561c 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx @@ -20,7 +20,6 @@ import { ViewMode, EmbeddableOutput, isErrorEmbeddable } from '../../../../'; import { AddPanelAction } from './add_panel_action'; import { EmbeddableFactory } from '../../../../embeddables'; -import { Filter, FilterStateStore } from '@kbn/es-query'; import { FILTERABLE_EMBEDDABLE, FilterableEmbeddable, @@ -32,6 +31,7 @@ import { GetEmbeddableFactory } from '../../../../types'; // eslint-disable-next-line import { coreMock } from '../../../../../../../../core/public/mocks'; import { ContactCardEmbeddable } from '../../../../test_samples'; +import { esFilters } from '../../../../../../../../plugins/data/public'; const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); @@ -51,8 +51,8 @@ beforeEach(async () => { () => null ); - const derivedFilter: Filter = { - $state: { store: FilterStateStore.APP_STATE }, + const derivedFilter: esFilters.Filter = { + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, alias: 'name', negate: false }, query: { match: {} }, }; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx index 550f9706a634b5..8d9beec940acc0 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx @@ -28,7 +28,6 @@ import { } from '../../../test_samples'; // eslint-disable-next-line import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; -import { FilterStateStore } from '@kbn/es-query'; import { EmbeddableFactory, EmbeddableOutput, @@ -37,6 +36,7 @@ import { } from '../../../embeddables'; import { GetEmbeddableFactory } from '../../../types'; import { of } from '../../../../tests/helpers'; +import { esFilters } from '../../../../../../../plugins/data/public'; const setup = async () => { const embeddableFactories = new Map(); @@ -48,7 +48,7 @@ const setup = async () => { panels: {}, filters: [ { - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, alias: 'name', negate: false }, query: { match: {} }, }, diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx index 22e3be89f1ae93..684a8c45a4e890 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx @@ -17,7 +17,6 @@ * under the License. */ -import { Filter, FilterStateStore } from '@kbn/es-query'; import { EmbeddableOutput, isErrorEmbeddable } from '../../../'; import { RemovePanelAction } from './remove_panel_action'; import { EmbeddableFactory } from '../../../embeddables'; @@ -30,6 +29,7 @@ import { FilterableEmbeddableFactory } from '../../../test_samples/embeddables/f import { FilterableContainer } from '../../../test_samples/embeddables/filterable_container'; import { GetEmbeddableFactory, ViewMode } from '../../../types'; import { ContactCardEmbeddable } from '../../../test_samples/embeddables/contact_card/contact_card_embeddable'; +import { esFilters } from '../../../../../../../plugins/data/public'; const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); @@ -39,8 +39,8 @@ let container: FilterableContainer; let embeddable: FilterableEmbeddable; beforeEach(async () => { - const derivedFilter: Filter = { - $state: { store: FilterStateStore.APP_STATE }, + const derivedFilter: esFilters.Filter = { + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, alias: 'name', negate: false }, query: { match: {} }, }; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx index eaef8048a6fbff..de708b778c3c7c 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx @@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -import { Filter } from '@kbn/es-query'; + import { Container, ContainerInput } from '../../containers'; import { GetEmbeddableFactory } from '../../types'; +import { esFilters } from '../../../../../data/public'; export const FILTERABLE_CONTAINER = 'FILTERABLE_CONTAINER'; export interface FilterableContainerInput extends ContainerInput { - filters: Filter[]; + filters: esFilters.Filter[]; } /** @@ -33,7 +34,7 @@ export interface FilterableContainerInput extends ContainerInput { */ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type InheritedChildrenInput = { - filters: Filter[]; + filters: esFilters.Filter[]; id?: string; }; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx index f6885ca25b437a..56aa7688f37a69 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable.tsx @@ -17,14 +17,14 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { IContainer } from '../../containers'; import { EmbeddableOutput, EmbeddableInput, Embeddable } from '../../embeddables'; +import { esFilters } from '../../../../../data/public'; export const FILTERABLE_EMBEDDABLE = 'FILTERABLE_EMBEDDABLE'; export interface FilterableEmbeddableInput extends EmbeddableInput { - filters: Filter[]; + filters: esFilters.Filter[]; } export class FilterableEmbeddable extends Embeddable { diff --git a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts index 52500acc3dc59d..0721acb1a1fba9 100644 --- a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts +++ b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts @@ -32,7 +32,7 @@ import { } from '../lib/test_samples'; // eslint-disable-next-line import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; -import { FilterStateStore } from '@kbn/es-query'; +import { esFilters } from '../../../../plugins/data/public'; test('ApplyFilterAction applies the filter to the root of the container tree', async () => { const { doStart } = testPlugin(); @@ -76,7 +76,7 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a } const filter: any = { - $state: { store: FilterStateStore.APP_STATE }, + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, negate: false, diff --git a/src/plugins/embeddable/public/tests/container.test.ts b/src/plugins/embeddable/public/tests/container.test.ts index 3bdbcbad857d65..f97c26a41b901b 100644 --- a/src/plugins/embeddable/public/tests/container.test.ts +++ b/src/plugins/embeddable/public/tests/container.test.ts @@ -26,7 +26,6 @@ import { FILTERABLE_EMBEDDABLE, } from '../lib/test_samples/embeddables/filterable_embeddable'; import { ERROR_EMBEDDABLE_TYPE } from '../lib/embeddables/error_embeddable'; -import { Filter, FilterStateStore } from '@kbn/es-query'; import { FilterableEmbeddableFactory } from '../lib/test_samples/embeddables/filterable_embeddable_factory'; import { CONTACT_CARD_EMBEDDABLE } from '../lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory'; import { SlowContactCardEmbeddableFactory } from '../lib/test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory'; @@ -46,6 +45,7 @@ import { import { coreMock } from '../../../../core/public/mocks'; import { testPlugin } from './test_plugin'; import { of } from './helpers'; +import { esFilters } from '../../../../plugins/data/public'; async function creatHelloWorldContainerAndEmbeddable( containerInput: ContainerInput = { id: 'hello', panels: {} }, @@ -437,8 +437,8 @@ test('Test nested reactions', async done => { test('Explicit embeddable input mapped to undefined will default to inherited', async () => { const { start } = await creatHelloWorldContainerAndEmbeddable(); - const derivedFilter: Filter = { - $state: { store: FilterStateStore.APP_STATE }, + const derivedFilter: esFilters.Filter = { + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, alias: 'name', negate: false }, query: { match: {} }, }; diff --git a/src/plugins/embeddable/public/tests/explicit_input.test.ts b/src/plugins/embeddable/public/tests/explicit_input.test.ts index 6cde7bdc48ba17..47c4b0944cef22 100644 --- a/src/plugins/embeddable/public/tests/explicit_input.test.ts +++ b/src/plugins/embeddable/public/tests/explicit_input.test.ts @@ -18,7 +18,6 @@ */ import { skip } from 'rxjs/operators'; -import { Filter, FilterStateStore } from '@kbn/es-query'; import { testPlugin } from './test_plugin'; import { FILTERABLE_EMBEDDABLE, @@ -34,6 +33,7 @@ import { isErrorEmbeddable } from '../lib'; import { HelloWorldContainer } from '../lib/test_samples/embeddables/hello_world_container'; // eslint-disable-next-line import { coreMock } from '../../../../core/public/mocks'; +import { esFilters } from '../../../../plugins/data/public'; const { setup, doStart, coreStart, uiActions } = testPlugin( coreMock.createSetup(), @@ -50,8 +50,8 @@ setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, factory); setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE_TYPE, new HelloWorldEmbeddableFactory()); test('Explicit embeddable input mapped to undefined will default to inherited', async () => { - const derivedFilter: Filter = { - $state: { store: FilterStateStore.APP_STATE }, + const derivedFilter: esFilters.Filter = { + $state: { store: esFilters.FilterStateStore.APP_STATE }, meta: { disabled: false, alias: 'name', negate: false }, query: { match: {} }, }; diff --git a/src/plugins/expressions/public/expression_types/kibana_context.ts b/src/plugins/expressions/public/expression_types/kibana_context.ts index 174517abc2c053..bcf8e2853dec88 100644 --- a/src/plugins/expressions/public/expression_types/kibana_context.ts +++ b/src/plugins/expressions/public/expression_types/kibana_context.ts @@ -17,8 +17,7 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; -import { TimeRange, Query } from 'src/plugins/data/public'; +import { TimeRange, Query, esFilters } from 'src/plugins/data/public'; const name = 'kibana_context'; export type KIBANA_CONTEXT_NAME = 'kibana_context'; @@ -26,7 +25,7 @@ export type KIBANA_CONTEXT_NAME = 'kibana_context'; export interface KibanaContext { type: typeof name; query?: Query | Query[]; - filters?: Filter[]; + filters?: esFilters.Filter[]; timeRange?: TimeRange; } diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index 2adb0d77941599..2d66216a9770ba 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -17,13 +17,13 @@ * under the License. */ -import { Filter } from '@kbn/es-query'; import { ExpressionInterpret } from '../interpreter_provider'; import { TimeRange } from '../../../data/public'; import { Adapters } from '../../../inspector/public'; import { Query } from '../../../data/public'; import { ExpressionAST } from '../../../expressions/public'; import { ExpressionArgAST } from '../../../../plugins/expressions/public'; +import { esFilters } from '../../../../plugins/data/public'; export { ArgumentType } from './arguments'; export { @@ -76,7 +76,7 @@ export type Context = object; export interface SearchContext { type: 'kibana_context'; - filters?: Filter[]; + filters?: esFilters.Filter[]; query?: Query; timeRange?: TimeRange; } diff --git a/src/plugins/kibana_react/public/context/context.test.tsx b/src/plugins/kibana_react/public/context/context.test.tsx index d7dce3f69239dd..4008daf69f25d4 100644 --- a/src/plugins/kibana_react/public/context/context.test.tsx +++ b/src/plugins/kibana_react/public/context/context.test.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { context, createKibanaReactContext, useKibana, KibanaContextProvider } from './context'; import { coreMock, overlayServiceMock } from '../../../../core/public/mocks'; -import { CoreStart } from './types'; +import { CoreStart } from '../../../../core/public'; let container: HTMLDivElement | null; diff --git a/src/plugins/kibana_react/public/context/types.ts b/src/plugins/kibana_react/public/context/types.ts index 1906bb808ea87b..35e6349bfca87c 100644 --- a/src/plugins/kibana_react/public/context/types.ts +++ b/src/plugins/kibana_react/public/context/types.ts @@ -22,8 +22,6 @@ import { CoreStart } from '../../../../core/public'; import { KibanaReactOverlays } from '../overlays'; import { KibanaReactNotifications } from '../notifications'; -export { CoreStart }; - export type KibanaServices = Partial; export interface KibanaReactContextValue { diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx b/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx index 81d514ca4d466d..a880d3c6cf87c3 100644 --- a/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx +++ b/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx @@ -24,11 +24,11 @@ import { EuiScreenReaderOnly, keyCodes } from '@elastic/eui'; // @ts-ignore import { KuiButton } from '@kbn/ui-framework/components'; -interface Props { +export interface ExitFullScreenButtonProps { onExitFullScreenMode: () => void; } -class ExitFullScreenButtonUi extends PureComponent { +class ExitFullScreenButtonUi extends PureComponent { public onKeyDown = (e: KeyboardEvent) => { if (e.keyCode === keyCodes.ESCAPE) { this.props.onExitFullScreenMode(); diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/index.tsx b/src/plugins/kibana_react/public/exit_full_screen_button/index.tsx index a965fd776e0c29..130ac1e980eee3 100644 --- a/src/plugins/kibana_react/public/exit_full_screen_button/index.tsx +++ b/src/plugins/kibana_react/public/exit_full_screen_button/index.tsx @@ -17,4 +17,4 @@ * under the License. */ -export { ExitFullScreenButton } from './exit_full_screen_button'; +export { ExitFullScreenButton, ExitFullScreenButtonProps } from './exit_full_screen_button'; diff --git a/src/plugins/kibana_react/public/overlays/types.ts b/src/plugins/kibana_react/public/overlays/types.ts index 6a1fb25ca14835..0108d8eaba6cc9 100644 --- a/src/plugins/kibana_react/public/overlays/types.ts +++ b/src/plugins/kibana_react/public/overlays/types.ts @@ -18,7 +18,7 @@ */ import * as React from 'react'; -import { CoreStart } from '../context/types'; +import { CoreStart } from '../../../../core/public'; export interface KibanaReactOverlays { openFlyout: ( diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_finder.tsx b/src/plugins/kibana_react/public/saved_objects/saved_object_finder.tsx index fbea5d613f60ca..c65d4289587670 100644 --- a/src/plugins/kibana_react/public/saved_objects/saved_object_finder.tsx +++ b/src/plugins/kibana_react/public/saved_objects/saved_object_finder.tsx @@ -104,7 +104,7 @@ interface SavedObjectFinderInitialPageSize extends BaseSavedObjectFinder { initialPageSize?: 5 | 10 | 15 | 25; fixedPageSize?: undefined; } -type SavedObjectFinderProps = { +export type SavedObjectFinderProps = { savedObjects: CoreStart['savedObjects']; uiSettings: CoreStart['uiSettings']; } & (SavedObjectFinderFixedPage | SavedObjectFinderInitialPageSize); diff --git a/test/accessibility/apps/discover.ts b/test/accessibility/apps/discover.ts new file mode 100644 index 00000000000000..e3f73ad4bcaf86 --- /dev/null +++ b/test/accessibility/apps/discover.ts @@ -0,0 +1,46 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +const FROM_TIME = '2015-09-19 06:31:44.000'; +const TO_TIME = '2015-09-23 18:31:44.000'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'timePicker']); + const a11y = getService('a11y'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + describe('Discover', () => { + before(async () => { + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.update({ + defaultIndex: 'logstash-*', + }); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setAbsoluteRange(FROM_TIME, TO_TIME); + }); + + it('main view', async () => { + await a11y.testAppSnapshot(); + }); + }); +} diff --git a/test/accessibility/apps/home.ts b/test/accessibility/apps/home.ts new file mode 100644 index 00000000000000..4df4d5f42c93d8 --- /dev/null +++ b/test/accessibility/apps/home.ts @@ -0,0 +1,35 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common']); + const a11y = getService('a11y'); + + describe('Kibana Home', () => { + before(async () => { + await PageObjects.common.navigateToApp('home'); + }); + + it('Kibana Home view', async () => { + await a11y.testAppSnapshot(); + }); + }); +} diff --git a/test/accessibility/apps/management.ts b/test/accessibility/apps/management.ts new file mode 100644 index 00000000000000..842f4ecbafa9ef --- /dev/null +++ b/test/accessibility/apps/management.ts @@ -0,0 +1,55 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'settings']); + const a11y = getService('a11y'); + + describe('Management', () => { + before(async () => { + await PageObjects.common.navigateToApp('settings'); + }); + + it('main view', async () => { + await a11y.testAppSnapshot(); + }); + + it('index pattern page', async () => { + await PageObjects.settings.clickKibanaIndexPatterns(); + await a11y.testAppSnapshot(); + }); + + it('Single indexpattern view', async () => { + await PageObjects.settings.clickIndexPatternLogstash(); + await a11y.testAppSnapshot(); + }); + + it('Saved objects view', async () => { + await PageObjects.settings.clickKibanaSavedObjects(); + await a11y.testAppSnapshot(); + }); + + it('Advanced settings', async () => { + await PageObjects.settings.clickKibanaSettings(); + await a11y.testAppSnapshot(); + }); + }); +} diff --git a/packages/kbn-es-query/src/filters/exists.js b/test/accessibility/config.ts similarity index 58% rename from packages/kbn-es-query/src/filters/exists.js rename to test/accessibility/config.ts index 0c82279fb44176..d73a73820a1172 100644 --- a/packages/kbn-es-query/src/filters/exists.js +++ b/test/accessibility/config.ts @@ -17,14 +17,26 @@ * under the License. */ -// Creates a filter where the given field exists -export function buildExistsFilter(field, indexPattern) { +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +export default async function({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + return { - meta: { - index: indexPattern.id + ...functionalConfig.getAll(), + + testFiles: [ + require.resolve('./apps/discover'), + require.resolve('./apps/management'), + require.resolve('./apps/home'), + ], + pageObjects, + services, + + junit: { + reportName: 'Accessibility Tests', }, - exists: { - field: field.name - } }; } diff --git a/test/api_integration/services/chance.js b/test/accessibility/ftr_provider_context.d.ts similarity index 76% rename from test/api_integration/services/chance.js rename to test/accessibility/ftr_provider_context.d.ts index 3c5d9dc704aae0..22df3b16150a43 100644 --- a/test/api_integration/services/chance.js +++ b/test/accessibility/ftr_provider_context.d.ts @@ -17,13 +17,9 @@ * under the License. */ -import Chance from 'chance'; +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; -export function ChanceProvider({ getService }) { - const log = getService('log'); +import { pageObjects } from './page_objects'; +import { services } from './services'; - const seed = Date.now(); - log.debug('randomness seed: %j', seed); - - return new Chance(seed); -} +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/test/accessibility/page_objects.ts b/test/accessibility/page_objects.ts new file mode 100644 index 00000000000000..151b6b34781b84 --- /dev/null +++ b/test/accessibility/page_objects.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 { pageObjects } from '../functional/page_objects'; diff --git a/test/accessibility/services/a11y/a11y.ts b/test/accessibility/services/a11y/a11y.ts new file mode 100644 index 00000000000000..7adfe7ebfcc7d7 --- /dev/null +++ b/test/accessibility/services/a11y/a11y.ts @@ -0,0 +1,130 @@ +/* + * 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 chalk from 'chalk'; +import testSubjectToCss from '@kbn/test-subj-selector'; + +import { FtrProviderContext } from '../../ftr_provider_context'; +import { AxeReport, printResult } from './axe_report'; +// @ts-ignore JS that is run in browser as is +import { analyzeWithAxe, analyzeWithAxeWithClient } from './analyze_with_axe'; + +interface AxeContext { + include?: string[]; + exclude?: string[][]; +} + +interface TestOptions { + excludeTestSubj?: string | string[]; +} + +export const normalizeResult = (report: any) => { + if (report.error) { + throw report.error; + } + + return report.result as false | AxeReport; +}; + +export function A11yProvider({ getService }: FtrProviderContext) { + const browser = getService('browser'); + const Wd = getService('__webdriver__'); + const log = getService('log'); + + /** + * Accessibility testing service using the Axe (https://www.deque.com/axe/) + * toolset to validate a11y rules similar to ESLint. In order to test against + * the rules we must load up the UI and feed a full HTML snapshot into Axe. + */ + return new (class Accessibility { + public async testAppSnapshot(options: TestOptions = {}) { + const context = this.getAxeContext(true, options.excludeTestSubj); + const report = await this.captureAxeReport(context); + await this.testAxeReport(report); + } + + public async testGlobalSnapshot(options: TestOptions = {}) { + const context = this.getAxeContext(false, options.excludeTestSubj); + const report = await this.captureAxeReport(context); + await this.testAxeReport(report); + } + + private getAxeContext(global: boolean, excludeTestSubj?: string | string[]): AxeContext { + return { + include: global ? undefined : [testSubjectToCss('appA11yRoot')], + exclude: ([] as string[]) + .concat(excludeTestSubj || []) + .map(ts => [testSubjectToCss(ts)]) + .concat([['.ace_scrollbar']]), + }; + } + + private testAxeReport(report: AxeReport) { + const errorMsgs = []; + + for (const result of report.incomplete) { + // these items require human review and can't be definitively validated + log.warning(printResult(chalk.yellow('UNABLE TO VALIDATE'), result)); + } + + for (const result of report.violations) { + errorMsgs.push(printResult(chalk.red('VIOLATION'), result)); + } + + if (errorMsgs.length) { + throw new Error(`a11y report:\n${errorMsgs.join('\n')}`); + } + } + + private async captureAxeReport(context: AxeContext): Promise { + const axeOptions = { + reporter: 'v2', + runOnly: ['wcag2a', 'wcag2aa'], + rules: { + 'color-contrast': { + enabled: false, + }, + }, + }; + + await (Wd.driver.manage() as any).setTimeouts({ + ...(await (Wd.driver.manage() as any).getTimeouts()), + script: 600000, + }); + + const report = normalizeResult( + await browser.executeAsync(analyzeWithAxe, context, axeOptions) + ); + + if (report !== false) { + return report; + } + + const withClientReport = normalizeResult( + await browser.executeAsync(analyzeWithAxeWithClient, context, axeOptions) + ); + + if (withClientReport === false) { + throw new Error('Attempted to analyze with axe but failed to load axe client'); + } + + return withClientReport; + } + })(); +} diff --git a/test/accessibility/services/a11y/analyze_with_axe.js b/test/accessibility/services/a11y/analyze_with_axe.js new file mode 100644 index 00000000000000..5cba55a29f8a4d --- /dev/null +++ b/test/accessibility/services/a11y/analyze_with_axe.js @@ -0,0 +1,39 @@ +/* + * 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 { readFileSync } from 'fs'; + +export function analyzeWithAxe(context, options, callback) { + Promise.resolve() + .then(() => { + if (window.axe) { + return window.axe.run(context, options); + } + + // return a false report to trigger analyzeWithAxeWithClient + return false; + }) + .then(result => callback({ result }), error => callback({ error })); +} + +export const analyzeWithAxeWithClient = ` + ${readFileSync(require.resolve('axe-core/axe.js'), 'utf8')} + + return (${analyzeWithAxe.toString()}).apply(null, arguments); +`; diff --git a/test/accessibility/services/a11y/axe_report.ts b/test/accessibility/services/a11y/axe_report.ts new file mode 100644 index 00000000000000..ba1e8c739079da --- /dev/null +++ b/test/accessibility/services/a11y/axe_report.ts @@ -0,0 +1,69 @@ +/* + * 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. + */ + +type AxeImpact = 'minor' | 'moderate' | 'serious' | 'critical'; + +type AxeRelatedNodes = Array<{ + data: any; + id: string; + impact: AxeImpact; + message: string; + relatedNodes: []; +}>; + +export interface AxeResult { + /* Rule description */ + description: string; + /* rule title/error message */ + help: string; + /* documentation url */ + helpUrl: string; + /* rule id */ + id: string; + /* severity level */ + impact?: AxeImpact; + /* tags used to group rules */ + tags: string[]; + /* nodes grouped in this result */ + nodes: Array<{ + all: AxeRelatedNodes; + any: AxeRelatedNodes; + none: AxeRelatedNodes; + + html: string; + impact: AxeImpact; + target: string[]; + }>; +} + +export type AxeResultGroup = AxeResult[]; + +export interface AxeReport { + inapplicable: AxeResultGroup; + passes: AxeResultGroup; + incomplete: AxeResultGroup; + violations: AxeResultGroup; +} + +export const printResult = (title: string, result: AxeResult) => ` +${title} + [${result.id}]: ${result.description} + Help: ${result.helpUrl} + Elements: + - ${result.nodes.map(node => node.target).join('\n - ')}`; diff --git a/src/legacy/server/index_patterns/service/index.ts b/test/accessibility/services/a11y/index.ts similarity index 95% rename from src/legacy/server/index_patterns/service/index.ts rename to test/accessibility/services/a11y/index.ts index 496c4ba7b5b6f1..7baa17c1eafa33 100644 --- a/src/legacy/server/index_patterns/service/index.ts +++ b/test/accessibility/services/a11y/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from './index_patterns_service'; +export { A11yProvider } from './a11y'; diff --git a/test/accessibility/services/index.ts b/test/accessibility/services/index.ts new file mode 100644 index 00000000000000..98d1628732f5c8 --- /dev/null +++ b/test/accessibility/services/index.ts @@ -0,0 +1,26 @@ +/* + * 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 { services as kibanaFunctionalServices } from '../../functional/services'; +import { A11yProvider } from './a11y'; + +export const services = { + ...kibanaFunctionalServices, + a11y: A11yProvider, +}; diff --git a/test/api_integration/apis/core/index.js b/test/api_integration/apis/core/index.js index e5da4e4730662f..d617b2ad073511 100644 --- a/test/api_integration/apis/core/index.js +++ b/test/api_integration/apis/core/index.js @@ -16,45 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - describe('core', () => { - describe('request context', () => { - it('provides access to elasticsearch', async () => ( - await supertest - .get('/requestcontext/elasticsearch') - .expect(200, 'Elasticsearch: true') - )); + describe('core request context', () => { + it('provides access to elasticsearch', async () => ( + await supertest + .get('/requestcontext/elasticsearch') + .expect(200, 'Elasticsearch: true') + )); - it('provides access to SavedObjects client', async () => ( - await supertest - .get('/requestcontext/savedobjectsclient') - .expect(200, 'SavedObjects client: {"page":1,"per_page":20,"total":0,"saved_objects":[]}') - )); - }); - - describe('compression', () => { - it(`uses compression when there isn't a referer`, async () => { - await supertest - .get('/app/kibana') - .set('accept-encoding', 'gzip') - .then(response => { - expect(response.headers).to.have.property('content-encoding', 'gzip'); - }); - }); - - it(`doesn't use compression when there is a referer`, async () => { - await supertest - .get('/app/kibana') - .set('accept-encoding', 'gzip') - .set('referer', 'https://www.google.com') - .then(response => { - expect(response.headers).not.to.have.property('content-encoding'); - }); - }); - }); + it('provides access to SavedObjects client', async () => ( + await supertest + .get('/requestcontext/savedobjectsclient') + .expect(200, 'SavedObjects client: {"page":1,"per_page":20,"total":0,"saved_objects":[]}') + )); }); } diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index de36ee678b10ed..9f2672959390ce 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -34,6 +34,5 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./status')); loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); - loadTestFile(require.resolve('./core')); }); } diff --git a/test/api_integration/apis/index_patterns/es_errors/errors.js b/test/api_integration/apis/index_patterns/es_errors/errors.js index 7e04e3f7204d71..4e50b965211c29 100644 --- a/test/api_integration/apis/index_patterns/es_errors/errors.js +++ b/test/api_integration/apis/index_patterns/es_errors/errors.js @@ -26,7 +26,7 @@ import { createNoMatchingIndicesError, isNoMatchingIndicesError, convertEsError -} from '../../../../../src/legacy/server/index_patterns/service/lib/errors'; +} from '../../../../../src/plugins/data/server/index_patterns/fetcher/lib/errors'; import { getIndexNotFoundError, diff --git a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/params.js b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/params.js index 37916d3bee3759..2bd574d669e20e 100644 --- a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/params.js +++ b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/params.js @@ -20,7 +20,7 @@ export default function ({ getService }) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const chance = getService('chance'); + const randomness = getService('randomness'); describe('params', () => { before(() => esArchiver.load('index_patterns/basic_index')); @@ -63,8 +63,8 @@ export default function ({ getService }) { supertest .get('/api/index_patterns/_fields_for_wildcard') .query({ - pattern: chance.word(), - [chance.word()]: chance.word(), + pattern: randomness.word(), + [randomness.word()]: randomness.word(), }) .expect(400)); }); diff --git a/test/api_integration/services/index.ts b/test/api_integration/services/index.ts index d0fcd94a6a2047..573450cae534a4 100644 --- a/test/api_integration/services/index.ts +++ b/test/api_integration/services/index.ts @@ -22,14 +22,11 @@ import { services as commonServices } from '../../common/services'; // @ts-ignore not TS yet import { KibanaSupertestProvider, ElasticsearchSupertestProvider } from './supertest'; -// @ts-ignore not TS yet -import { ChanceProvider } from './chance'; - export const services = { es: commonServices.es, esArchiver: commonServices.esArchiver, retry: commonServices.retry, supertest: KibanaSupertestProvider, esSupertest: ElasticsearchSupertestProvider, - chance: ChanceProvider, + randomness: commonServices.randomness, }; diff --git a/test/common/services/index.ts b/test/common/services/index.ts index 47d93d811e3f6b..225aacc1c98958 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -21,10 +21,12 @@ import { LegacyEsProvider } from './legacy_es'; import { EsArchiverProvider } from './es_archiver'; import { KibanaServerProvider } from './kibana_server'; import { RetryProvider } from './retry'; +import { RandomnessProvider } from './randomness'; export const services = { es: LegacyEsProvider, esArchiver: EsArchiverProvider, kibanaServer: KibanaServerProvider, retry: RetryProvider, + randomness: RandomnessProvider, }; diff --git a/test/common/services/randomness.ts b/test/common/services/randomness.ts new file mode 100644 index 00000000000000..2a49703e92c58a --- /dev/null +++ b/test/common/services/randomness.ts @@ -0,0 +1,89 @@ +/* + * 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 Chance from 'chance'; + +import { FtrProviderContext } from '../ftr_provider_context'; + +interface CharOptions { + pool?: string; + alpha?: boolean; + numeric?: boolean; + symbols?: boolean; + casing?: 'lower' | 'upper'; +} + +interface StringOptions extends CharOptions { + length?: number; +} + +interface NumberOptions { + min?: number; + max?: number; +} + +export function RandomnessProvider({ getService }: FtrProviderContext) { + const log = getService('log'); + + const seed = Date.now(); + log.debug('randomness seed: %j', seed); + + const chance = new Chance(seed); + + return new (class RandomnessService { + /** + * Generate a random natural number + * + * range: 0 to 9007199254740991 + * + */ + naturalNumber(options?: NumberOptions) { + return chance.natural(options); + } + + /** + * Generate a random integer + */ + integer(options?: NumberOptions) { + return chance.integer(options); + } + + /** + * Generate a random number, defaults to at least 4 and no more than 8 syllables + */ + word(options: { syllables?: number } = {}) { + const { syllables = this.naturalNumber({ min: 4, max: 8 }) } = options; + + return chance.word({ + syllables, + }); + } + + /** + * Generate a random string, defaults to at least 8 and no more than 15 alpha-numeric characters + */ + string(options: StringOptions = {}) { + return chance.string({ + length: this.naturalNumber({ min: 8, max: 15 }), + ...(options.pool === 'undefined' ? { alpha: true, numeric: true, symbols: false } : {}), + ...options, + }); + } + })(); +} diff --git a/test/functional/apps/dashboard/dashboard_state.js b/test/functional/apps/dashboard/dashboard_state.js index 6dfef4c54c66b5..b9ad47e428e829 100644 --- a/test/functional/apps/dashboard/dashboard_state.js +++ b/test/functional/apps/dashboard/dashboard_state.js @@ -24,7 +24,7 @@ import { PIE_CHART_VIS_NAME, AREA_CHART_VIS_NAME } from '../../page_objects/dash // eslint-disable-next-line import { DEFAULT_PANEL_WIDTH -} from '../../../../src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_constants'; +} from '../../../../src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_constants'; export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'discover']); diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js index 4654b85f9c7c45..8d12b2117a2149 100644 --- a/test/functional/page_objects/discover_page.js +++ b/test/functional/page_objects/discover_page.js @@ -283,19 +283,13 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { } async openSidebarFieldFilter() { - const fieldFilterFormExists = await testSubjects.exists('discoverFieldFilter'); - if (!fieldFilterFormExists) { - await testSubjects.click('toggleFieldFilterButton'); - await testSubjects.existOrFail('discoverFieldFilter'); - } + await testSubjects.click('toggleFieldFilterButton'); + await testSubjects.existOrFail('filterSelectionPanel'); } async closeSidebarFieldFilter() { - const fieldFilterFormExists = await testSubjects.exists('discoverFieldFilter'); - if (fieldFilterFormExists) { - await testSubjects.click('toggleFieldFilterButton'); - await testSubjects.missingOrFail('discoverFieldFilter', { allowHidden: true }); - } + await testSubjects.click('toggleFieldFilterButton'); + await testSubjects.missingOrFail('filterSelectionPanel', { allowHidden: true }); } } diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 4060e3dd7800f2..97e02958f3787f 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -461,10 +461,7 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { ); } - public async executeAsync( - fn: string | ((...args: A) => R), - ...args: A - ): Promise { + public async executeAsync(fn: string | ((...args: any[]) => R), ...args: any[]): Promise { return await driver.executeAsyncScript( fn, ...cloneDeep(args, arg => { diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts index 5ebec6bde6ebbd..201354fff48efa 100644 --- a/test/functional/services/remote/webdriver.ts +++ b/test/functional/services/remote/webdriver.ts @@ -75,13 +75,23 @@ async function attemptToCreateCommand( case 'chrome': { const chromeCapabilities = Capabilities.chrome(); const chromeOptions = [ - 'disable-translate', - 'new-window', + // Disables the sandbox for all process types that are normally sandboxed. 'no-sandbox', + // Launches URL in new browser window. + 'new-window', + // By default, file:// URIs cannot read other file:// URIs. This is an override for developers who need the old behavior for testing. 'allow-file-access-from-files', + // Use fake device for Media Stream to replace actual camera and microphone. 'use-fake-device-for-media-stream', + // Bypass the media stream infobar by selecting the default device for media streams (e.g. WebRTC). Works with --use-fake-device-for-media-stream. 'use-fake-ui-for-media-stream', ]; + if (process.platform === 'linux') { + // The /dev/shm partition is too small in certain VM environments, causing + // Chrome to fail or crash. Use this flag to work-around this issue + // (a temporary directory will always be used to create anonymous shared memory files). + chromeOptions.push('disable-dev-shm-usage'); + } if (headlessBrowser === '1') { // Use --disable-gpu to avoid an error from a missing Mesa library, as per // See: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md diff --git a/test/scripts/jenkins_accessibility.sh b/test/scripts/jenkins_accessibility.sh new file mode 100755 index 00000000000000..0b3d8dc3f85c23 --- /dev/null +++ b/test/scripts/jenkins_accessibility.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "$IS_PIPELINE_JOB" ]] ; then + source src/dev/ci_setup/setup_env.sh +fi + +export TEST_BROWSER_HEADLESS=1 + +if [[ -z "$IS_PIPELINE_JOB" ]] ; then + yarn run grunt functionalTests:ensureAllTestsInCiGroup; + node scripts/build --debug --oss; +else + installDir="$(realpath $PARENT_DIR/kibana/build/oss/kibana-*-SNAPSHOT-linux-x86_64)" + destDir=${installDir}-${CI_WORKER_NUMBER} + cp -R "$installDir" "$destDir" + + export KIBANA_INSTALL_DIR="$destDir" +fi + +checks-reporter-with-killswitch "Kibana accessibility tests" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$installDir" \ + --config test/accessibility/config.ts; diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh index 274cca695b5357..c6bddb69aa5709 100755 --- a/test/scripts/jenkins_ci_group.sh +++ b/test/scripts/jenkins_ci_group.sh @@ -1,12 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi - -export TEST_BROWSER_HEADLESS=1 +source test/scripts/jenkins_test_setup.sh if [[ -z "$IS_PIPELINE_JOB" ]] ; then yarn run grunt functionalTests:ensureAllTestsInCiGroup; diff --git a/test/scripts/jenkins_firefox_smoke.sh b/test/scripts/jenkins_firefox_smoke.sh index 69cedc9657340b..9a31f5f43d2242 100755 --- a/test/scripts/jenkins_firefox_smoke.sh +++ b/test/scripts/jenkins_firefox_smoke.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi +source test/scripts/jenkins_test_setup.sh if [[ -z "$IS_PIPELINE_JOB" ]] ; then node scripts/build --debug --oss; diff --git a/test/scripts/jenkins_test_setup.sh b/test/scripts/jenkins_test_setup.sh new file mode 100644 index 00000000000000..e2dd0bc276bb64 --- /dev/null +++ b/test/scripts/jenkins_test_setup.sh @@ -0,0 +1,20 @@ +set -e + +function post_work() { + set +e + if [[ -z "$IS_PIPELINE_JOB" ]] ; then + node "$KIBANA_DIR/scripts/report_failed_tests" + fi + + if [[ -z "$REMOVE_KIBANA_INSTALL_DIR" && -z "$KIBANA_INSTALL_DIR" && -d "$KIBANA_INSTALL_DIR" ]]; then + rm -rf "$REMOVE_KIBANA_INSTALL_DIR" + fi +} + +trap 'post_work' EXIT + +export TEST_BROWSER_HEADLESS=1 + +if [[ -n "$IS_PIPELINE_JOB" ]] ; then + source src/dev/ci_setup/setup_env.sh +fi diff --git a/test/scripts/jenkins_visual_regression.sh b/test/scripts/jenkins_visual_regression.sh index fdda24423d977a..9ca1c0f08d2c94 100755 --- a/test/scripts/jenkins_visual_regression.sh +++ b/test/scripts/jenkins_visual_regression.sh @@ -1,11 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi - +source test/scripts/jenkins_test_setup.sh source "$KIBANA_DIR/src/dev/ci_setup/setup_percy.sh" if [[ -z "$IS_PIPELINE_JOB" ]] ; then @@ -22,8 +17,6 @@ else export KIBANA_INSTALL_DIR="$destDir" fi -export TEST_BROWSER_HEADLESS=1 - checks-reporter-with-killswitch "Kibana visual regression tests" \ yarn run percy exec -t 500 \ node scripts/functional_tests \ diff --git a/test/scripts/jenkins_xpack_accessibility.sh b/test/scripts/jenkins_xpack_accessibility.sh new file mode 100755 index 00000000000000..af813c3c40f841 --- /dev/null +++ b/test/scripts/jenkins_xpack_accessibility.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "$IS_PIPELINE_JOB" ]] ; then + source src/dev/ci_setup/setup_env.sh +fi + +if [[ -z "$IS_PIPELINE_JOB" ]] ; then + echo " -> building and extracting default Kibana distributable for use in functional tests" + node scripts/build --debug --no-oss + + linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" + installDir="$PARENT_DIR/install/kibana" + + mkdir -p "$installDir" + tar -xzf "$linuxBuild" -C "$installDir" --strip=1 + + export KIBANA_INSTALL_DIR="$installDir" +else + installDir="$PARENT_DIR/install/kibana" + destDir="${installDir}-${CI_WORKER_NUMBER}" + cp -R "$installDir" "$destDir" + + export KIBANA_INSTALL_DIR="$destDir" +fi + +export TEST_BROWSER_HEADLESS=1 +cd "$XPACK_DIR" + +checks-reporter-with-killswitch "X-Pack accessibility tests" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$installDir" \ + --config test/accessibility/config.ts; diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh index 0b9666b33decab..fba05f8f252d76 100755 --- a/test/scripts/jenkins_xpack_ci_group.sh +++ b/test/scripts/jenkins_xpack_ci_group.sh @@ -1,12 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi - -export TEST_BROWSER_HEADLESS=1 +source test/scripts/jenkins_test_setup.sh if [[ -z "$IS_PIPELINE_JOB" ]] ; then echo " -> Ensuring all functional tests are in a ciGroup" diff --git a/test/scripts/jenkins_xpack_firefox_smoke.sh b/test/scripts/jenkins_xpack_firefox_smoke.sh index 18ed468de1317a..43220459bcb97d 100755 --- a/test/scripts/jenkins_xpack_firefox_smoke.sh +++ b/test/scripts/jenkins_xpack_firefox_smoke.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi +source test/scripts/jenkins_test_setup.sh if [[ -z "$IS_PIPELINE_JOB" ]] ; then node scripts/build --debug --no-oss; @@ -21,8 +17,6 @@ else export KIBANA_INSTALL_DIR="$destDir" fi -export TEST_BROWSER_HEADLESS=1 - cd "$XPACK_DIR" checks-reporter-with-killswitch "X-Pack firefox smoke test" \ diff --git a/test/scripts/jenkins_xpack_visual_regression.sh b/test/scripts/jenkins_xpack_visual_regression.sh index 0f3275d45f13b2..5699f9e5ee7c14 100755 --- a/test/scripts/jenkins_xpack_visual_regression.sh +++ b/test/scripts/jenkins_xpack_visual_regression.sh @@ -1,11 +1,6 @@ #!/usr/bin/env bash -set -e - -if [[ -n "$IS_PIPELINE_JOB" ]] ; then - source src/dev/ci_setup/setup_env.sh -fi - +source test/scripts/jenkins_test_setup.sh source "$KIBANA_DIR/src/dev/ci_setup/setup_percy.sh" if [[ -z "$IS_PIPELINE_JOB" ]] ; then @@ -23,8 +18,6 @@ else export KIBANA_INSTALL_DIR="$destDir" fi -export TEST_BROWSER_HEADLESS=1 - cd "$XPACK_DIR" checks-reporter-with-killswitch "X-Pack visual regression tests" \ diff --git a/test/visual_regression/tests/discover/chart_visualization.js b/test/visual_regression/tests/discover/chart_visualization.js index 5467f2d3eaa145..4e1d9bf643cf6d 100644 --- a/test/visual_regression/tests/discover/chart_visualization.js +++ b/test/visual_regression/tests/discover/chart_visualization.js @@ -29,6 +29,7 @@ export default function ({ getService, getPageObjects }) { const visualTesting = getService('visualTesting'); const defaultSettings = { defaultIndex: 'logstash-*', + 'discover:sampleSize': 1 }; describe('discover', function describeIndexTests() { diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 0ee50c0caa3402..6d0da2f0b693d3 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -34,6 +34,7 @@ "xpack.server": "legacy/server", "xpack.snapshotRestore": "legacy/plugins/snapshot_restore", "xpack.spaces": ["legacy/plugins/spaces", "plugins/spaces"], + "xpack.taskManager": "legacy/plugins/task_manager", "xpack.transform": "legacy/plugins/transform", "xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant", "xpack.uptime": "legacy/plugins/uptime", diff --git a/x-pack/legacy/plugins/actions/index.ts b/x-pack/legacy/plugins/actions/index.ts index 7c4dd9f73c11f4..a58c936c637492 100644 --- a/x-pack/legacy/plugins/actions/index.ts +++ b/x-pack/legacy/plugins/actions/index.ts @@ -22,10 +22,10 @@ export function actions(kibana: any) { return new kibana.Plugin({ id: 'actions', configPrefix: 'xpack.actions', - require: ['kibana', 'elasticsearch', 'task_manager', 'encrypted_saved_objects'], + require: ['kibana', 'elasticsearch', 'task_manager', 'encryptedSavedObjects'], isEnabled(config: Legacy.KibanaConfig) { return ( - config.get('xpack.encrypted_saved_objects.enabled') === true && + config.get('xpack.encryptedSavedObjects.enabled') === true && config.get('xpack.actions.enabled') === true && config.get('xpack.task_manager.enabled') === true ); diff --git a/x-pack/legacy/plugins/actions/server/actions_client.test.ts b/x-pack/legacy/plugins/actions/server/actions_client.test.ts index b4940b23ba61c9..933b7b0b239b68 100644 --- a/x-pack/legacy/plugins/actions/server/actions_client.test.ts +++ b/x-pack/legacy/plugins/actions/server/actions_client.test.ts @@ -11,9 +11,14 @@ import { ActionsClient } from './actions_client'; import { ExecutorType } from './types'; import { ActionExecutor, TaskRunnerFactory } from './lib'; import { taskManagerMock } from '../../task_manager/task_manager.mock'; -import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, +} from '../../../../../src/core/server/mocks'; +const defaultKibanaIndex = '.kibana'; const savedObjectsClient = savedObjectsClientMock.create(); +const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const mockTaskManager = taskManagerMock.create(); @@ -22,11 +27,22 @@ const actionTypeRegistryParams = { taskRunnerFactory: new TaskRunnerFactory(new ActionExecutor()), }; +let actionsClient: ActionsClient; +let actionTypeRegistry: ActionTypeRegistry; const executor: ExecutorType = async options => { return { status: 'ok' }; }; -beforeEach(() => jest.resetAllMocks()); +beforeEach(() => { + jest.resetAllMocks(); + actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionsClient = new ActionsClient({ + actionTypeRegistry, + savedObjectsClient, + scopedClusterClient, + defaultKibanaIndex, + }); +}); describe('create()', () => { test('creates an action with all given properties', async () => { @@ -40,16 +56,11 @@ describe('create()', () => { }, references: [], }; - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', executor, }); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); const result = await actionsClient.create({ action: { @@ -80,11 +91,6 @@ describe('create()', () => { }); test('validates config', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', @@ -110,11 +116,6 @@ describe('create()', () => { }); test(`throws an error when an action type doesn't exist`, async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); await expect( actionsClient.create({ action: { @@ -130,16 +131,11 @@ describe('create()', () => { }); test('encrypts action type options unless specified not to', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', executor, }); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: 'type', @@ -198,11 +194,6 @@ describe('create()', () => { describe('get()', () => { test('calls savedObjectsClient with id', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.get.mockResolvedValueOnce({ id: '1', type: 'type', @@ -242,12 +233,12 @@ describe('find()', () => { }, ], }; - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.find.mockResolvedValueOnce(expectedResult); + scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ + aggregations: { + '1': { doc_count: 6 }, + }, + }); const result = await actionsClient.find({}); expect(result).toEqual({ total: 1, @@ -259,6 +250,7 @@ describe('find()', () => { config: { foo: 'bar', }, + referencedByCount: 6, }, ], }); @@ -276,11 +268,6 @@ describe('find()', () => { describe('delete()', () => { test('calls savedObjectsClient with id', async () => { const expectedResult = Symbol(); - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.delete.mockResolvedValueOnce(expectedResult); const result = await actionsClient.delete({ id: '1' }); expect(result).toEqual(expectedResult); @@ -296,16 +283,11 @@ describe('delete()', () => { describe('update()', () => { test('updates an action with all given properties', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', executor, }); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.get.mockResolvedValueOnce({ id: '1', type: 'action', @@ -362,11 +344,6 @@ describe('update()', () => { }); test('validates config', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', @@ -400,16 +377,11 @@ describe('update()', () => { }); test('encrypts action type options unless specified not to', async () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', executor, }); - const actionsClient = new ActionsClient({ - actionTypeRegistry, - savedObjectsClient, - }); savedObjectsClient.get.mockResolvedValueOnce({ id: 'my-action', type: 'action', diff --git a/x-pack/legacy/plugins/actions/server/actions_client.ts b/x-pack/legacy/plugins/actions/server/actions_client.ts index 823cb55abaf5e7..1e4135bd0d66fd 100644 --- a/x-pack/legacy/plugins/actions/server/actions_client.ts +++ b/x-pack/legacy/plugins/actions/server/actions_client.ts @@ -4,10 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, SavedObjectAttributes, SavedObject } from 'src/core/server'; +import { + IScopedClusterClient, + SavedObjectsClientContract, + SavedObjectAttributes, + SavedObject, +} from 'src/core/server'; + import { ActionTypeRegistry } from './action_type_registry'; import { validateConfig, validateSecrets } from './lib'; -import { ActionResult } from './types'; +import { ActionResult, FindActionResult, RawAction } from './types'; interface ActionUpdate extends SavedObjectAttributes { description: string; @@ -44,10 +50,12 @@ interface FindResult { page: number; perPage: number; total: number; - data: ActionResult[]; + data: FindActionResult[]; } interface ConstructorOptions { + defaultKibanaIndex: string; + scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; savedObjectsClient: SavedObjectsClientContract; } @@ -58,12 +66,21 @@ interface UpdateOptions { } export class ActionsClient { + private readonly defaultKibanaIndex: string; + private readonly scopedClusterClient: IScopedClusterClient; private readonly savedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; - constructor({ actionTypeRegistry, savedObjectsClient }: ConstructorOptions) { + constructor({ + actionTypeRegistry, + defaultKibanaIndex, + scopedClusterClient, + savedObjectsClient, + }: ConstructorOptions) { this.actionTypeRegistry = actionTypeRegistry; this.savedObjectsClient = savedObjectsClient; + this.scopedClusterClient = scopedClusterClient; + this.defaultKibanaIndex = defaultKibanaIndex; } /** @@ -134,16 +151,22 @@ export class ActionsClient { * Find actions */ public async find({ options = {} }: FindOptions): Promise { - const findResult = await this.savedObjectsClient.find({ + const findResult = await this.savedObjectsClient.find({ ...options, type: 'action', }); + const data = await injectExtraFindData( + this.defaultKibanaIndex, + this.scopedClusterClient, + findResult.saved_objects.map(actionFromSavedObject) + ); + return { page: findResult.page, perPage: findResult.per_page, total: findResult.total, - data: findResult.saved_objects.map(actionFromSavedObject), + data, }; } @@ -155,9 +178,64 @@ export class ActionsClient { } } -function actionFromSavedObject(savedObject: SavedObject) { +function actionFromSavedObject(savedObject: SavedObject): ActionResult { return { id: savedObject.id, ...savedObject.attributes, }; } + +async function injectExtraFindData( + defaultKibanaIndex: string, + scopedClusterClient: IScopedClusterClient, + actionResults: ActionResult[] +): Promise { + const aggs: Record = {}; + for (const actionResult of actionResults) { + aggs[actionResult.id] = { + filter: { + bool: { + must: { + nested: { + path: 'references', + query: { + bool: { + filter: { + bool: { + must: [ + { + term: { + 'references.id': actionResult.id, + }, + }, + { + term: { + 'references.type': 'action', + }, + }, + ], + }, + }, + }, + }, + }, + }, + }, + }, + }; + } + const aggregationResult = await scopedClusterClient.callAsInternalUser('search', { + index: defaultKibanaIndex, + body: { + aggs, + size: 0, + query: { + match_all: {}, + }, + }, + }); + return actionResults.map(actionResult => ({ + ...actionResult, + referencedByCount: aggregationResult.aggregations[actionResult.id].doc_count, + })); +} diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/lib/result_type.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/lib/result_type.ts index c891f3bf218e7d..256463251315d8 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/lib/result_type.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/lib/result_type.ts @@ -8,16 +8,15 @@ // Which is basically the Haskel equivalent of Rust/ML/Scala's Result // I'll reach out to other's in Kibana to see if we can merge these into one type -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type Ok = { +export interface Ok { tag: 'ok'; value: T; -}; -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type Err = { +} + +export interface Err { tag: 'err'; error: E; -}; +} export type Result = Ok | Err; export function asOk(value: T): Ok { diff --git a/x-pack/legacy/plugins/actions/server/lib/action_executor.test.ts b/x-pack/legacy/plugins/actions/server/lib/action_executor.test.ts index c724717bef9eb8..5ed67ae82b0ce5 100644 --- a/x-pack/legacy/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/legacy/plugins/actions/server/lib/action_executor.test.ts @@ -8,7 +8,7 @@ import Hapi from 'hapi'; import { schema } from '@kbn/config-schema'; import { ActionExecutor } from './action_executor'; import { actionTypeRegistryMock } from '../action_type_registry.mock'; -import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/plugin.mock'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; import { savedObjectsClientMock, loggingServiceMock, @@ -24,7 +24,7 @@ function getServices() { callCluster: jest.fn(), }; } -const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.create(); +const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); const actionTypeRegistry = actionTypeRegistryMock.create(); const executeParams = { diff --git a/x-pack/legacy/plugins/actions/server/lib/action_executor.ts b/x-pack/legacy/plugins/actions/server/lib/action_executor.ts index aef389262f884d..b7d4ea96ea0f88 100644 --- a/x-pack/legacy/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/legacy/plugins/actions/server/lib/action_executor.ts @@ -5,7 +5,7 @@ */ import Hapi from 'hapi'; -import { EncryptedSavedObjectsStartContract } from '../shim'; +import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; import { LegacySpacesPlugin as SpacesPluginStartContract } from '../../../spaces'; import { Logger } from '../../../../../../src/core/server'; import { validateParams, validateConfig, validateSecrets } from './validate_with_schema'; diff --git a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts index 6411bc7462c9f2..3e71725713070d 100644 --- a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.test.ts @@ -11,7 +11,7 @@ import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager'; import { TaskRunnerFactory } from './task_runner_factory'; import { actionTypeRegistryMock } from '../action_type_registry.mock'; import { actionExecutorMock } from './action_executor.mock'; -import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/plugin.mock'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; import { savedObjectsClientMock, loggingServiceMock, @@ -19,7 +19,7 @@ import { const spaceIdToNamespace = jest.fn(); const actionTypeRegistry = actionTypeRegistryMock.create(); -const mockedEncryptedSavedObjectsPlugin = encryptedSavedObjectsMock.create(); +const mockedEncryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); const mockedActionExecutor = actionExecutorMock.create(); let fakeTimer: sinon.SinonFakeTimers; diff --git a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.ts b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.ts index efe9ce7fa8be5f..c0cca22b2c3eba 100644 --- a/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.ts +++ b/x-pack/legacy/plugins/actions/server/lib/task_runner_factory.ts @@ -7,7 +7,7 @@ import { ActionExecutorContract } from './action_executor'; import { ExecutorError } from './executor_error'; import { RunContext } from '../../../task_manager'; -import { EncryptedSavedObjectsStartContract } from '../shim'; +import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; import { ActionTaskParams, GetBasePathFunction, SpaceIdToNamespaceFunction } from '../types'; export interface TaskRunnerContext { diff --git a/x-pack/legacy/plugins/actions/server/plugin.ts b/x-pack/legacy/plugins/actions/server/plugin.ts index 618c1d120c37ae..26b65b1a1689a6 100644 --- a/x-pack/legacy/plugins/actions/server/plugin.ts +++ b/x-pack/legacy/plugins/actions/server/plugin.ts @@ -22,6 +22,7 @@ import { ActionsCoreStart, ActionsPluginsSetup, ActionsPluginsStart, + KibanaConfig, } from './shim'; import { createActionRoute, @@ -44,6 +45,7 @@ export interface PluginStartContract { } export class Plugin { + private readonly kibana$: Observable; private readonly config$: Observable; private readonly logger: Logger; private serverBasePath?: string; @@ -51,10 +53,12 @@ export class Plugin { private taskRunnerFactory?: TaskRunnerFactory; private actionTypeRegistry?: ActionTypeRegistry; private actionExecutor?: ActionExecutor; + private defaultKibanaIndex?: string; constructor(initializerContext: ActionsPluginInitializerContext) { this.logger = initializerContext.logger.get('plugins', 'alerting'); this.config$ = initializerContext.config.create(); + this.kibana$ = initializerContext.config.kibana$; } public async setup( @@ -63,6 +67,7 @@ export class Plugin { ): Promise { const config = await this.config$.pipe(first()).toPromise(); this.adminClient = await core.elasticsearch.adminClient$.pipe(first()).toPromise(); + this.defaultKibanaIndex = (await this.kibana$.pipe(first()).toPromise()).index; plugins.xpack_main.registerFeature({ id: 'actions', @@ -92,12 +97,12 @@ export class Plugin { // - `secrets` properties will be encrypted // - `config` will be included in AAD // - everything else excluded from AAD - plugins.encrypted_saved_objects.registerType({ + plugins.encryptedSavedObjects.registerType({ type: 'action', attributesToEncrypt: new Set(['secrets']), attributesToExcludeFromAAD: new Set(['description']), }); - plugins.encrypted_saved_objects.registerType({ + plugins.encryptedSavedObjects.registerType({ type: 'action_task_params', attributesToEncrypt: new Set(['apiKey']), }); @@ -141,6 +146,7 @@ export class Plugin { adminClient, serverBasePath, taskRunnerFactory, + defaultKibanaIndex, } = this; function getServices(request: any): Services { @@ -163,11 +169,11 @@ export class Plugin { logger, spaces: plugins.spaces, getServices, - encryptedSavedObjectsPlugin: plugins.encrypted_saved_objects, + encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, actionTypeRegistry: actionTypeRegistry!, }); taskRunnerFactory!.initialize({ - encryptedSavedObjectsPlugin: plugins.encrypted_saved_objects, + encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, getBasePath, spaceIdToNamespace, }); @@ -186,6 +192,8 @@ export class Plugin { return new ActionsClient({ savedObjectsClient, actionTypeRegistry: actionTypeRegistry!, + defaultKibanaIndex: defaultKibanaIndex!, + scopedClusterClient: adminClient!.asScoped(request), }); }, }; diff --git a/x-pack/legacy/plugins/actions/server/routes/_mock_server.ts b/x-pack/legacy/plugins/actions/server/routes/_mock_server.ts index 7f2341c1aa010e..23356cedb3ab8c 100644 --- a/x-pack/legacy/plugins/actions/server/routes/_mock_server.ts +++ b/x-pack/legacy/plugins/actions/server/routes/_mock_server.ts @@ -8,7 +8,7 @@ import Hapi from 'hapi'; import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; import { actionsClientMock } from '../actions_client.mock'; import { actionTypeRegistryMock } from '../action_type_registry.mock'; -import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/plugin.mock'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; const defaultConfig = { 'kibana.index': '.kibana', @@ -22,7 +22,8 @@ export function createMockServer(config: Record = defaultConfig) { const actionsClient = actionsClientMock.create(); const actionTypeRegistry = actionTypeRegistryMock.create(); const savedObjectsClient = savedObjectsClientMock.create(); - const encryptedSavedObjects = encryptedSavedObjectsMock.create(); + const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); + const encryptedSavedObjectsStart = encryptedSavedObjectsMock.createStart(); server.config = () => { return { @@ -49,21 +50,16 @@ export function createMockServer(config: Record = defaultConfig) { }, }); - server.register({ - name: 'encrypted_saved_objects', - register(pluginServer: Hapi.Server) { - pluginServer.expose('isEncryptionError', encryptedSavedObjects.isEncryptionError); - pluginServer.expose('registerType', encryptedSavedObjects.registerType); - pluginServer.expose( - 'getDecryptedAsInternalUser', - encryptedSavedObjects.getDecryptedAsInternalUser - ); - }, - }); - server.decorate('request', 'getSavedObjectsClient', () => savedObjectsClient); server.decorate('request', 'getActionsClient', () => actionsClient); server.decorate('request', 'getBasePath', () => '/s/my-space'); - return { server, savedObjectsClient, actionsClient, actionTypeRegistry, encryptedSavedObjects }; + return { + server, + savedObjectsClient, + actionsClient, + actionTypeRegistry, + encryptedSavedObjectsSetup, + encryptedSavedObjectsStart, + }; } diff --git a/x-pack/legacy/plugins/actions/server/shim.ts b/x-pack/legacy/plugins/actions/server/shim.ts index c457a40a78b679..0da6b84f2cc691 100644 --- a/x-pack/legacy/plugins/actions/server/shim.ts +++ b/x-pack/legacy/plugins/actions/server/shim.ts @@ -12,7 +12,10 @@ import { TaskManager } from '../../task_manager'; import { XPackMainPlugin } from '../../xpack_main/xpack_main'; import KbnServer from '../../../../../src/legacy/server/kbn_server'; import { LegacySpacesPlugin as SpacesPluginStartContract } from '../../spaces'; -import { EncryptedSavedObjectsPlugin } from '../../encrypted_saved_objects'; +import { + PluginSetupContract as EncryptedSavedObjectsSetupContract, + PluginStartContract as EncryptedSavedObjectsStartContract, +} from '../../../../plugins/encrypted_saved_objects/server'; import { PluginSetupContract as SecurityPlugin } from '../../../../plugins/security/server'; import { CoreSetup, @@ -24,13 +27,16 @@ import { // due to being marked as dependencies interface Plugins extends Hapi.PluginProperties { task_manager: TaskManager; - encrypted_saved_objects: EncryptedSavedObjectsPlugin; } export interface Server extends Legacy.Server { plugins: Plugins; } +export interface KibanaConfig { + index: string; +} + /** * Shim what we're thinking setup and start contracts will look like */ @@ -38,15 +44,10 @@ export type TaskManagerStartContract = Pick; export type SecurityPluginSetupContract = Pick; export type SecurityPluginStartContract = Pick; -export type EncryptedSavedObjectsSetupContract = Pick; export type TaskManagerSetupContract = Pick< TaskManager, 'addMiddleware' | 'registerTaskDefinitions' >; -export type EncryptedSavedObjectsStartContract = Pick< - EncryptedSavedObjectsPlugin, - 'isEncryptionError' | 'getDecryptedAsInternalUser' ->; /** * New platform interfaces @@ -54,6 +55,7 @@ export type EncryptedSavedObjectsStartContract = Pick< export interface ActionsPluginInitializerContext { logger: LoggerFactory; config: { + kibana$: Rx.Observable; create(): Rx.Observable; }; } @@ -73,12 +75,12 @@ export interface ActionsPluginsSetup { security?: SecurityPluginSetupContract; task_manager: TaskManagerSetupContract; xpack_main: XPackMainPluginSetupContract; - encrypted_saved_objects: EncryptedSavedObjectsSetupContract; + encryptedSavedObjects: EncryptedSavedObjectsSetupContract; } export interface ActionsPluginsStart { security?: SecurityPluginStartContract; spaces: () => SpacesPluginStartContract | undefined; - encrypted_saved_objects: EncryptedSavedObjectsStartContract; + encryptedSavedObjects: EncryptedSavedObjectsStartContract; task_manager: TaskManagerStartContract; } @@ -101,6 +103,9 @@ export function shim( const initializerContext: ActionsPluginInitializerContext = { logger: newPlatform.coreContext.logger, config: { + kibana$: Rx.of({ + index: server.config().get('kibana.index'), + }), create() { return Rx.of({ enabled: server.config().get('xpack.actions.enabled') as boolean, @@ -126,7 +131,8 @@ export function shim( security: newPlatform.setup.plugins.security as SecurityPluginSetupContract | undefined, task_manager: server.plugins.task_manager, xpack_main: server.plugins.xpack_main, - encrypted_saved_objects: server.plugins.encrypted_saved_objects, + encryptedSavedObjects: newPlatform.setup.plugins + .encryptedSavedObjects as EncryptedSavedObjectsSetupContract, }; const pluginsStart: ActionsPluginsStart = { @@ -134,7 +140,8 @@ export function shim( // TODO: Currently a function because it's an optional dependency that // initializes after this function is called spaces: () => server.plugins.spaces, - encrypted_saved_objects: server.plugins.encrypted_saved_objects, + encryptedSavedObjects: newPlatform.start.plugins + .encryptedSavedObjects as EncryptedSavedObjectsStartContract, task_manager: server.plugins.task_manager, }; diff --git a/x-pack/legacy/plugins/actions/server/types.ts b/x-pack/legacy/plugins/actions/server/types.ts index 9db89aea1b38d5..1ee5022338a465 100644 --- a/x-pack/legacy/plugins/actions/server/types.ts +++ b/x-pack/legacy/plugins/actions/server/types.ts @@ -45,6 +45,10 @@ export interface ActionResult { config: Record; } +export interface FindActionResult extends ActionResult { + referencedByCount: number; +} + // the result returned from an action type executor function export interface ActionTypeExecutorResult { status: 'ok' | 'error'; diff --git a/x-pack/legacy/plugins/alerting/README.md b/x-pack/legacy/plugins/alerting/README.md index ffe3185328abdb..1b90cb78d870c1 100644 --- a/x-pack/legacy/plugins/alerting/README.md +++ b/x-pack/legacy/plugins/alerting/README.md @@ -198,6 +198,7 @@ Payload: |Property|Description|Type| |---|---|---| |enabled|Indicate if you want the alert to start executing on an interval basis after it has been created.|boolean| +|name|A name to reference and search in the future.|string| |alertTypeId|The id value of the alert type you want to call when the alert is scheduled to execute.|string| |interval|The interval in seconds, minutes, hours or days the alert should execute. Example: `10s`, `5m`, `1h`, `1d`.|string| |alertTypeParams|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| @@ -242,6 +243,7 @@ Payload: |Property|Description|Type| |---|---|---| |interval|The interval in seconds, minutes, hours or days the alert should execute. Example: `10s`, `5m`, `1h`, `1d`.|string| +|name|A name to reference and search in the future.|string| |alertTypeParams|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object| |actions|Array of the following:
- `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value.
- `id` (string): The id of the action saved object to execute.
- `params` (object): There map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array| diff --git a/x-pack/legacy/plugins/alerting/index.ts b/x-pack/legacy/plugins/alerting/index.ts index 22db92baa8d28c..b3e33f782688c2 100644 --- a/x-pack/legacy/plugins/alerting/index.ts +++ b/x-pack/legacy/plugins/alerting/index.ts @@ -22,12 +22,12 @@ export function alerting(kibana: any) { return new kibana.Plugin({ id: 'alerting', configPrefix: 'xpack.alerting', - require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encrypted_saved_objects'], + require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'], isEnabled(config: Legacy.KibanaConfig) { return ( config.get('xpack.alerting.enabled') === true && config.get('xpack.actions.enabled') === true && - config.get('xpack.encrypted_saved_objects.enabled') === true && + config.get('xpack.encryptedSavedObjects.enabled') === true && config.get('xpack.task_manager.enabled') === true ); }, diff --git a/x-pack/legacy/plugins/alerting/mappings.json b/x-pack/legacy/plugins/alerting/mappings.json index bc648e874cfa4c..ff60ccc98cbaa6 100644 --- a/x-pack/legacy/plugins/alerting/mappings.json +++ b/x-pack/legacy/plugins/alerting/mappings.json @@ -4,6 +4,9 @@ "enabled": { "type": "boolean" }, + "name": { + "type": "text" + }, "alertTypeId": { "type": "keyword" }, diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts index 093f5f7484004b..99af7db2ef8d75 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts @@ -43,6 +43,7 @@ const mockedDate = new Date('2019-02-12T21:01:22.479Z'); function getMockData(overwrites: Record = {}) { return { enabled: true, + name: 'abc', alertTypeId: '123', interval: '10s', throttle: null, @@ -172,6 +173,7 @@ describe('create()', () => { "interval": "10s", "muteAll": false, "mutedInstanceIds": Array [], + "name": "abc", "throttle": null, "updatedBy": "elastic", } @@ -504,6 +506,7 @@ describe('create()', () => { }, ], alertTypeId: '123', + name: 'abc', alertTypeParams: { bar: true }, apiKey: Buffer.from('123:abc').toString('base64'), apiKeyOwner: 'elastic', @@ -1173,6 +1176,7 @@ describe('update()', () => { id: '1', data: { interval: '10s', + name: 'abc', alertTypeParams: { bar: true, }, @@ -1230,6 +1234,7 @@ describe('update()', () => { "apiKeyOwner": null, "enabled": true, "interval": "10s", + "name": "abc", "scheduledTaskId": "task-123", "updatedBy": "elastic", } @@ -1304,6 +1309,7 @@ describe('update()', () => { id: '1', data: { interval: '10s', + name: 'abc', alertTypeParams: { bar: true, }, @@ -1362,6 +1368,7 @@ describe('update()', () => { "apiKeyOwner": "elastic", "enabled": true, "interval": "10s", + "name": "abc", "scheduledTaskId": "task-123", "updatedBy": "elastic", } @@ -1406,6 +1413,7 @@ describe('update()', () => { id: '1', data: { interval: '10s', + name: 'abc', alertTypeParams: { bar: true, }, diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts index 39d0277ff53b3c..b92af43a1f1c63 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts @@ -72,6 +72,7 @@ interface CreateOptions { interface UpdateOptions { id: string; data: { + name: string; interval: string; actions: AlertAction[]; alertTypeParams: Record; diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts index 23591692bca1f5..dcc74ed9488cea 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { AlertExecutorOptions } from '../types'; import { ConcreteTaskInstance } from '../../../task_manager'; import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; -import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/plugin.mock'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; import { savedObjectsClientMock, loggingServiceMock, @@ -52,7 +52,7 @@ beforeAll(() => { afterAll(() => fakeTimer.restore()); const savedObjectsClient = savedObjectsClientMock.create(); -const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.create(); +const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); const services = { log: jest.fn(), callCluster: jest.fn(), diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts index ca11dc8533996d..0c6bd1b4a777a3 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts @@ -11,7 +11,7 @@ import { createAlertInstanceFactory } from './create_alert_instance_factory'; import { AlertInstance } from './alert_instance'; import { getNextRunAt } from './get_next_run_at'; import { validateAlertTypeParams } from './validate_alert_type_params'; -import { EncryptedSavedObjectsStartContract } from '../shim'; +import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; import { PluginStartContract as ActionsPluginStartContract } from '../../../actions'; import { AlertType, diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts index d6c6d5907e7ac4..c50bc795757f3e 100644 --- a/x-pack/legacy/plugins/alerting/server/plugin.ts +++ b/x-pack/legacy/plugins/alerting/server/plugin.ts @@ -85,7 +85,7 @@ export class Plugin { }); // Encrypted attributes - plugins.encrypted_saved_objects.registerType({ + plugins.encryptedSavedObjects.registerType({ type: 'alert', attributesToEncrypt: new Set(['apiKey']), attributesToExcludeFromAAD: new Set([ @@ -147,7 +147,7 @@ export class Plugin { }; }, executeAction: plugins.actions.execute, - encryptedSavedObjectsPlugin: plugins.encrypted_saved_objects, + encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects, spaceIdToNamespace(spaceId?: string): string | undefined { const spacesPlugin = plugins.spaces(); return spacesPlugin && spaceId ? spacesPlugin.spaceIdToNamespace(spaceId) : undefined; diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts index 3751aa968b3de8..bd153150849c8a 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts @@ -12,6 +12,7 @@ server.route(createAlertRoute); const mockedAlert = { alertTypeId: '1', + name: 'abc', interval: '10s', alertTypeParams: { bar: true, @@ -44,24 +45,25 @@ test('creates an alert with proper parameters', async () => { expect(statusCode).toBe(200); const response = JSON.parse(payload); expect(response).toMatchInlineSnapshot(` + Object { + "actions": Array [ Object { - "actions": Array [ - Object { - "group": "default", - "id": "2", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "1", - "alertTypeParams": Object { - "bar": true, + "group": "default", + "id": "2", + "params": Object { + "foo": true, }, - "id": "123", - "interval": "10s", - } - `); + }, + ], + "alertTypeId": "1", + "alertTypeParams": Object { + "bar": true, + }, + "id": "123", + "interval": "10s", + "name": "abc", + } + `); expect(alertsClient.create).toHaveBeenCalledTimes(1); expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -82,6 +84,7 @@ test('creates an alert with proper parameters', async () => { }, "enabled": true, "interval": "10s", + "name": "abc", "throttle": null, }, }, @@ -107,6 +110,7 @@ test('creates an alert with proper parameters', async () => { }, "enabled": true, "interval": "10s", + "name": "abc", "throttle": null, }, }, diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.ts b/x-pack/legacy/plugins/alerting/server/routes/create.ts index 984153d81e0f86..14f72b0041e76f 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/create.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/create.ts @@ -12,6 +12,7 @@ import { getDurationSchema } from '../lib'; interface ScheduleRequest extends Hapi.Request { payload: { enabled: boolean; + name: string; alertTypeId: string; interval: string; actions: AlertAction[]; @@ -32,6 +33,7 @@ export const createAlertRoute = { payload: Joi.object() .keys({ enabled: Joi.boolean().default(true), + name: Joi.string().required(), alertTypeId: Joi.string().required(), throttle: getDurationSchema().default(null), interval: getDurationSchema().required(), diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts index 9e4f18fa1b40d2..2237d8245097c0 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts @@ -36,6 +36,7 @@ test('calls the update function with proper parameters', async () => { url: '/api/alert/1', payload: { throttle: null, + name: 'abc', interval: '12s', alertTypeParams: { otherField: false, @@ -75,6 +76,7 @@ test('calls the update function with proper parameters', async () => { "otherField": false, }, "interval": "12s", + "name": "abc", "throttle": null, }, "id": "1", diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.ts b/x-pack/legacy/plugins/alerting/server/routes/update.ts index 2b95b7bc340542..09362295ae73bd 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/update.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/update.ts @@ -15,6 +15,7 @@ interface UpdateRequest extends Hapi.Request { }; payload: { alertTypeId: string; + name: string; interval: string; actions: AlertAction[]; alertTypeParams: Record; @@ -36,6 +37,7 @@ export const updateAlertRoute = { throttle: getDurationSchema() .required() .allow(null), + name: Joi.string().required(), interval: getDurationSchema().required(), alertTypeParams: Joi.object().required(), actions: Joi.array() diff --git a/x-pack/legacy/plugins/alerting/server/shim.ts b/x-pack/legacy/plugins/alerting/server/shim.ts index c977fda451df18..d86eab2038095d 100644 --- a/x-pack/legacy/plugins/alerting/server/shim.ts +++ b/x-pack/legacy/plugins/alerting/server/shim.ts @@ -10,7 +10,10 @@ import { LegacySpacesPlugin as SpacesPluginStartContract } from '../../spaces'; import { TaskManager } from '../../task_manager'; import { XPackMainPlugin } from '../../xpack_main/xpack_main'; import KbnServer from '../../../../../src/legacy/server/kbn_server'; -import { EncryptedSavedObjectsPlugin } from '../../encrypted_saved_objects'; +import { + PluginSetupContract as EncryptedSavedObjectsSetupContract, + PluginStartContract as EncryptedSavedObjectsStartContract, +} from '../../../../plugins/encrypted_saved_objects/server'; import { PluginSetupContract as SecurityPlugin } from '../../../../plugins/security/server'; import { CoreSetup, @@ -28,7 +31,6 @@ import { interface Plugins extends Hapi.PluginProperties { actions: ActionsPlugin; task_manager: TaskManager; - encrypted_saved_objects: EncryptedSavedObjectsPlugin; } export interface Server extends Legacy.Server { @@ -41,16 +43,11 @@ export interface Server extends Legacy.Server { export type TaskManagerStartContract = Pick; export type SecurityPluginSetupContract = Pick; export type SecurityPluginStartContract = Pick; -export type EncryptedSavedObjectsSetupContract = Pick; export type XPackMainPluginSetupContract = Pick; export type TaskManagerSetupContract = Pick< TaskManager, 'addMiddleware' | 'registerTaskDefinitions' >; -export type EncryptedSavedObjectsStartContract = Pick< - EncryptedSavedObjectsPlugin, - 'isEncryptionError' | 'getDecryptedAsInternalUser' ->; /** * New platform interfaces @@ -75,13 +72,13 @@ export interface AlertingPluginsSetup { task_manager: TaskManagerSetupContract; actions: ActionsPluginSetupContract; xpack_main: XPackMainPluginSetupContract; - encrypted_saved_objects: EncryptedSavedObjectsSetupContract; + encryptedSavedObjects: EncryptedSavedObjectsSetupContract; } export interface AlertingPluginsStart { actions: ActionsPluginStartContract; security?: SecurityPluginStartContract; spaces: () => SpacesPluginStartContract | undefined; - encrypted_saved_objects: EncryptedSavedObjectsStartContract; + encryptedSavedObjects: EncryptedSavedObjectsStartContract; task_manager: TaskManagerStartContract; } @@ -122,7 +119,8 @@ export function shim( task_manager: server.plugins.task_manager, actions: server.plugins.actions.setup, xpack_main: server.plugins.xpack_main, - encrypted_saved_objects: server.plugins.encrypted_saved_objects, + encryptedSavedObjects: newPlatform.setup.plugins + .encryptedSavedObjects as EncryptedSavedObjectsSetupContract, }; const pluginsStart: AlertingPluginsStart = { @@ -131,7 +129,8 @@ export function shim( // TODO: Currently a function because it's an optional dependency that // initializes after this function is called spaces: () => server.plugins.spaces, - encrypted_saved_objects: server.plugins.encrypted_saved_objects, + encryptedSavedObjects: newPlatform.start.plugins + .encryptedSavedObjects as EncryptedSavedObjectsStartContract, task_manager: server.plugins.task_manager, }; diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/legacy/plugins/alerting/server/types.ts index 3c71412da2c899..94b81c9e1b576d 100644 --- a/x-pack/legacy/plugins/alerting/server/types.ts +++ b/x-pack/legacy/plugins/alerting/server/types.ts @@ -60,6 +60,7 @@ export interface RawAlertAction extends SavedObjectAttributes { export interface Alert { enabled: boolean; + name: string; alertTypeId: string; interval: string; actions: AlertAction[]; @@ -76,6 +77,7 @@ export interface Alert { export interface RawAlert extends SavedObjectAttributes { enabled: boolean; + name: string; alertTypeId: string; interval: string; actions: RawAlertAction[]; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx b/x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx index 0c1d20d65b7b9f..cfbe8b1edbd719 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx @@ -12,11 +12,10 @@ import { RouteProps, withRouter } from 'react-router-dom'; -import { StringMap } from '../../../../typings/common'; import { RouteName } from './route_config/route_names'; type LocationMatch = Pick< - RouteComponentProps>, + RouteComponentProps>, 'location' | 'match' >; @@ -75,7 +74,7 @@ export function getBreadcrumb({ return null; } - const match = matchPath>(currentPath, route); + const match = matchPath>(currentPath, route); if (match) { return parse({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts index 4d6c3f4c116c96..c7860b81a7b1ec 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts @@ -7,7 +7,6 @@ import { isArray, isObject, isString } from 'lodash'; import mustache from 'mustache'; import uuid from 'uuid'; -import { StringMap } from '../../../../../../typings/common'; // @ts-ignore import * as rest from '../../../../../services/rest/watcher'; import { createErrorGroupWatch } from '../createErrorGroupWatch'; @@ -85,8 +84,11 @@ describe('createErrorGroupWatch', () => { }); // Recursively iterate a nested structure and render strings as mustache templates -type InputOutput = string | string[] | StringMap; -function renderMustache(input: InputOutput, ctx: StringMap): InputOutput { +type InputOutput = string | string[] | Record; +function renderMustache( + input: InputOutput, + ctx: Record +): InputOutput { if (isString(input)) { return mustache.render(input, { ctx, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts index 2617fef6de1d2b..e7d06403b8f8e1 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts @@ -17,7 +17,6 @@ import { PROCESSOR_EVENT, SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames'; -import { StringMap } from '../../../../../typings/common'; // @ts-ignore import { createWatch } from '../../../../services/rest/watcher'; @@ -50,8 +49,8 @@ interface Arguments { interface Actions { log_error: { logging: { text: string } }; - slack_webhook?: StringMap; - email?: StringMap; + slack_webhook?: Record; + email?: Record; } export async function createErrorGroupWatch({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 8fab7da377eb2c..67957ae76b1f10 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -22,7 +22,6 @@ import { import { useFetcher } from '../../../../hooks/useFetcher'; import { useCallApmApi } from '../../../../hooks/useCallApmApi'; import { APMClient } from '../../../../services/rest/createCallApmApi'; -import { StringMap } from '../../../../../typings/common'; import { useKibanaCore } from '../../../../../../observability/public'; const APM_INDEX_LABELS = [ @@ -79,7 +78,7 @@ async function saveApmIndices({ apmIndices }: { callApmApi: APMClient; - apmIndices: StringMap; + apmIndices: Record; }) { await callApmApi({ method: 'POST', @@ -95,7 +94,7 @@ export function ApmIndices() { notifications: { toasts } } = useKibanaCore(); - const [apmIndices, setApmIndices] = useState>({}); + const [apmIndices, setApmIndices] = useState>({}); const [isSaving, setIsSaving] = useState(false); const callApmApiFromHook = useCallApmApi(); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts index cc7697c1d89647..10f59e237ba7fd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -17,7 +17,6 @@ import { } from 'lodash'; import { idx } from '@kbn/elastic-idx'; import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace'; -import { StringMap } from '../../../../../../../../typings/common'; import { Span } from '../../../../../../../../typings/es_schemas/ui/Span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction'; @@ -191,7 +190,7 @@ function getServices(items: IWaterfallItem[]) { return uniq(serviceNames); } -export type IServiceColors = StringMap; +export type IServiceColors = Record; function getServiceColors(services: string[]) { const assignedColors = [ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx index 7f9d3c9f9f3b37..ed931191cfb963 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx @@ -6,10 +6,9 @@ import { EuiProgress } from '@elastic/eui'; import React from 'react'; -import { StringMap } from '../../../../typings/common'; // TODO: extend from EUI's EuiProgress prop interface -export interface ImpactBarProps extends StringMap { +export interface ImpactBarProps extends Record { value: number; max?: number; } diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts index b6caa47d1ae663..357ea23d522a01 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -6,13 +6,12 @@ import qs from 'querystring'; import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; -import { StringMap } from '../../../../typings/common'; export function toQuery(search?: string): APMQueryParamsRaw { return search ? qs.parse(search.slice(1)) : {}; } -export function fromQuery(query: StringMap) { +export function fromQuery(query: Record) { return qs.stringify(query, undefined, undefined, { encodeURIComponent: (value: string) => { return encodeURIComponent(value).replace(/%3A/g, ':'); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx index 0f5fcceea3d200..29a8528295dd78 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx @@ -8,7 +8,6 @@ import { EuiBasicTable } from '@elastic/eui'; import { sortByOrder } from 'lodash'; import React, { useMemo, useCallback, ReactNode } from 'react'; import { idx } from '@kbn/elastic-idx'; -import { StringMap } from '../../../../typings/common'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { history } from '../../../utils/history'; import { fromQuery, toQuery } from '../Links/url_helpers'; @@ -16,7 +15,7 @@ import { fromQuery, toQuery } from '../Links/url_helpers'; // TODO: this should really be imported from EUI export interface ITableColumn { name: ReactNode; - actions?: StringMap[]; + actions?: Array>; field?: string; dataType?: string; align?: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts index 0605c7dc8b02ac..dc815145db4ade 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts @@ -7,7 +7,8 @@ import { getTimezoneOffsetInMs } from './getTimezoneOffsetInMs'; import moment from 'moment-timezone'; -describe('getTimezoneOffsetInMs', () => { +// FAILING: https://github.com/elastic/kibana/issues/50005 +describe.skip('getTimezoneOffsetInMs', () => { describe('when no default timezone is set', () => { it('guesses the timezone', () => { const guess = jest.fn(() => 'Etc/UTC'); diff --git a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts b/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts index 4b65190d8ef222..b15231e89365a3 100644 --- a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts +++ b/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts @@ -11,7 +11,6 @@ import mean from 'lodash.mean'; import { rgba } from 'polished'; import { TimeSeriesAPIResponse } from '../../server/lib/transactions/charts'; import { ApmTimeSeriesResponse } from '../../server/lib/transactions/charts/get_timeseries_data/transform'; -import { StringMap } from '../../typings/common'; import { Coordinate, RectCoordinate, @@ -192,7 +191,7 @@ function getColorByKey(keys: string[]) { const assignedColors = ['HTTP 2xx', 'HTTP 3xx', 'HTTP 4xx', 'HTTP 5xx']; const unknownKeys = difference(keys, assignedColors); - const unassignedColors: StringMap = zipObject(unknownKeys, [ + const unassignedColors: Record = zipObject(unknownKeys, [ theme.euiColorVis1, theme.euiColorVis3, theme.euiColorVis4, diff --git a/x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts b/x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts index abe44a0fb39345..83e8f020e25291 100644 --- a/x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts +++ b/x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { StringMap } from '../../../typings/common'; - export class SessionStorageMock { - private store: StringMap = {}; + private store: Record = {}; public clear() { this.store = {}; diff --git a/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts b/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts index 146376665a0e69..95e283cd67709d 100644 --- a/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts +++ b/x-pack/legacy/plugins/apm/public/services/rest/xpack.ts @@ -5,7 +5,6 @@ */ import { HttpServiceBase } from 'kibana/public'; -import { StringMap } from '../../../typings/common'; import { callApi } from './callApi'; export interface LicenseApiResponse { @@ -13,11 +12,11 @@ export interface LicenseApiResponse { is_active: boolean; }; features: { - beats_management?: StringMap; - graph?: StringMap; - grokdebugger?: StringMap; - index_management?: StringMap; - logstash?: StringMap; + beats_management?: Record; + graph?: Record; + grokdebugger?: Record; + index_management?: Record; + logstash?: Record; ml?: { is_available: boolean; license_type: number; @@ -25,12 +24,12 @@ export interface LicenseApiResponse { enable_links: boolean; show_links: boolean; }; - reporting?: StringMap; - rollup?: StringMap; - searchprofiler?: StringMap; - security?: StringMap; - spaces?: StringMap; - tilemap?: StringMap; + reporting?: Record; + rollup?: Record; + searchprofiler?: Record; + security?: Record; + spaces?: Record; + tilemap?: Record; watcher?: { is_available: boolean; enable_links: boolean; diff --git a/x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts b/x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts index db1ed490eb7f20..130a7b52ea33be 100644 --- a/x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts +++ b/x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts @@ -5,8 +5,6 @@ */ import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { StringMap } from '../../typings/common'; - const { euiColorDarkShade, euiColorWarning } = theme; export const errorColor = '#c23c2b'; @@ -14,7 +12,7 @@ export const neutralColor = euiColorDarkShade; export const successColor = '#327a42'; export const warningColor = euiColorWarning; -const httpStatusCodeColors: StringMap = { +const httpStatusCodeColors: Record = { 1: neutralColor, 2: successColor, 3: neutralColor, diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts index 84c52b895f20d2..ee41599454dd69 100644 --- a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts @@ -14,7 +14,6 @@ import { import { Legacy } from 'kibana'; import { cloneDeep, has, isString, set, pick } from 'lodash'; import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames'; -import { StringMap, Omit } from '../../../typings/common'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { ESSearchResponse, @@ -95,7 +94,7 @@ interface APMOptions { export function getESClient(req: Legacy.Request) { const cluster = req.server.plugins.elasticsearch.getCluster('data'); - const query = req.query as StringMap; + const query = req.query as Record; return { search: async < diff --git a/x-pack/legacy/plugins/apm/server/lib/index_pattern/getKueryBarIndexPattern.ts b/x-pack/legacy/plugins/apm/server/lib/index_pattern/getKueryBarIndexPattern.ts index 33d404009ae204..a5ba4573cfd58a 100644 --- a/x-pack/legacy/plugins/apm/server/lib/index_pattern/getKueryBarIndexPattern.ts +++ b/x-pack/legacy/plugins/apm/server/lib/index_pattern/getKueryBarIndexPattern.ts @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import { StaticIndexPattern } from 'ui/index_patterns'; import { APICaller } from 'src/core/server'; -import { IndexPatternsService } from '../../../../../../../src/legacy/server/index_patterns/service'; +import { IndexPatternsFetcher } from '../../../../../../../src/plugins/data/server'; import { Setup } from '../helpers/setup_request'; export const getKueryBarIndexPattern = async ({ @@ -21,7 +21,7 @@ export const getKueryBarIndexPattern = async ({ }) => { const { indices } = setup; - const indexPatternsService = new IndexPatternsService( + const indexPatternsFetcher = new IndexPatternsFetcher( (...rest: Parameters) => request.server.plugins.elasticsearch .getCluster('data') @@ -40,7 +40,7 @@ export const getKueryBarIndexPattern = async ({ const configuredIndices = indexNames.map(name => indicesMap[name]); - const fields = await indexPatternsService.getFieldsForWildcard({ + const fields = await indexPatternsFetcher.getFieldsForWildcard({ pattern: configuredIndices }); diff --git a/x-pack/legacy/plugins/apm/typings/common.ts b/x-pack/legacy/plugins/apm/typings/common.ts index cfe6c9c572dc40..2fafceb32209c1 100644 --- a/x-pack/legacy/plugins/apm/typings/common.ts +++ b/x-pack/legacy/plugins/apm/typings/common.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface StringMap { - [key: string]: T; -} - // Allow unknown properties in an object export type AllowUnknownProperties = T extends Array ? Array> @@ -24,9 +20,3 @@ export type PromiseReturnType = Func extends ( ) => Promise ? Value : Func; - -export type IndexAsString = { - [k: string]: Map[keyof Map]; -} & Map; - -export type Omit = Pick>; diff --git a/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts b/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts index 657968fbd704f4..56cd0ff23a3fb5 100644 --- a/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts +++ b/x-pack/legacy/plugins/apm/typings/elasticsearch/index.ts @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Omit } from 'utility-types'; import { SearchParams, SearchResponse } from 'elasticsearch'; import { AggregationResponseMap, AggregationInputMap } from './aggregations'; -import { StringMap } from '../common'; export interface ESSearchBody { query?: any; @@ -55,6 +53,11 @@ export type ESSearchResponse< export interface ESFilter { [key: string]: { - [key: string]: string | string[] | number | StringMap | ESFilter[]; + [key: string]: + | string + | string[] + | number + | Record + | ESFilter[]; }; } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts index d4a42ba662db41..526728bd77cac6 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/adapters/elasticsearch/rest.ts @@ -7,16 +7,16 @@ import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { isEmpty } from 'lodash'; import { npStart } from 'ui/new_platform'; -import { RestAPIAdapter } from '../rest_api/adapter_types'; import { ElasticsearchAdapter } from './adapter_types'; import { AutocompleteSuggestion } from '../../../../../../../../src/plugins/data/public'; +import { setup as data } from '../../../../../../../../src/legacy/core_plugins/data/public/legacy'; const getAutocompleteProvider = (language: string) => npStart.plugins.data.autocomplete.getProvider(language); export class RestElasticsearchAdapter implements ElasticsearchAdapter { private cachedIndexPattern: any = null; - constructor(private readonly api: RestAPIAdapter, private readonly indexPatternName: string) {} + constructor(private readonly indexPatternName: string) {} public isKueryValid(kuery: string): boolean { try { @@ -65,9 +65,9 @@ export class RestElasticsearchAdapter implements ElasticsearchAdapter { if (this.cachedIndexPattern) { return this.cachedIndexPattern; } - const res = await this.api.get( - `/api/index_patterns/_fields_for_wildcard?pattern=${this.indexPatternName}` - ); + const res = await data.indexPatterns.indexPatterns.getFieldsForWildcard({ + pattern: this.indexPatternName, + }); if (isEmpty(res.fields)) { return; } diff --git a/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts b/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts index 3d8ca51e006bf7..2ebda89ba13fd6 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/compose/kibana.ts @@ -35,7 +35,7 @@ const onKibanaReady = chrome.dangerouslyGetActiveInjector; export function compose(): FrontendLibs { const api = new AxiosRestAPIAdapter(chrome.getXsrfToken(), chrome.getBasePath()); - const esAdapter = new RestElasticsearchAdapter(api, INDEX_NAMES.BEATS); + const esAdapter = new RestElasticsearchAdapter(INDEX_NAMES.BEATS); const elasticsearchLib = new ElasticsearchLib(esAdapter); const configBlocks = new ConfigBlocksLib( new RestConfigBlocksAdapter(api), diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts index 6efe6bc96dbba6..546e8967a74397 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts @@ -6,7 +6,7 @@ // @ts-ignore import { MAP_SAVED_OBJECT_TYPE } from '../../../maps/common/constants'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/visualize/embeddable'; +import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/visualize/embeddable/constants'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/embeddable/constants'; export const EmbeddableTypes = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts index 958d9c6a3a6f08..abaa16c4e32710 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Filter as ESFilterType } from '@kbn/es-query'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { TimeRange } from 'src/plugins/data/public'; import { EmbeddableInput } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; @@ -15,6 +14,7 @@ import { EmbeddableExpression, } from '../../expression_types'; import { getFunctionHelp } from '../../../i18n'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; interface Arguments { id: string; @@ -29,7 +29,7 @@ interface SavedMapInput extends EmbeddableInput { isPaused: boolean; interval: number; }; - filters: ESFilterType[]; + filters: esFilters.Filter[]; } type Return = EmbeddableExpression; diff --git a/x-pack/legacy/plugins/canvas/scripts/shareable_runtime.js b/x-pack/legacy/plugins/canvas/scripts/shareable_runtime.js index 760298c8a2dff9..f867e4dc77a119 100644 --- a/x-pack/legacy/plugins/canvas/scripts/shareable_runtime.js +++ b/x-pack/legacy/plugins/canvas/scripts/shareable_runtime.js @@ -91,10 +91,20 @@ run( clean(); log.info('Building Canvas Shareable Workpad Runtime...'); - execa.sync('yarn', ['webpack', '--config', webpackConfig, '--hide-modules', '--progress'], { - ...options, - env, - }); + execa.sync( + 'yarn', + [ + 'webpack', + '--config', + webpackConfig, + '--hide-modules', + ...(process.stdout.isTTY ? ['--progress'] : []), + ], + { + ...options, + env, + } + ); log.success('...runtime built!'); }, { diff --git a/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts b/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts index 8de813255a2301..ca34246531bff5 100644 --- a/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts +++ b/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildQueryFilter, Filter as ESFilterType } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/public'; import { Filter } from '../../types'; // @ts-ignore Untyped Local import { buildBoolArray } from './build_bool_array'; +import { esFilters } from '../../../../../../src/plugins/data/common'; export interface EmbeddableFilterInput { - filters: ESFilterType[]; + filters: esFilters.Filter[]; timeRange?: TimeRange; } @@ -30,8 +30,10 @@ function getTimeRangeFromFilters(filters: Filter[]): TimeRange | undefined { : undefined; } -function getQueryFilters(filters: Filter[]): ESFilterType[] { - return buildBoolArray(filters.filter(filter => filter.type !== 'time')).map(buildQueryFilter); +function getQueryFilters(filters: Filter[]): esFilters.Filter[] { + return buildBoolArray(filters.filter(filter => filter.type !== 'time')).map( + esFilters.buildQueryFilter + ); } export function buildEmbeddableFilters(filters: Filter[]): EmbeddableFilterInput { diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/__snapshots__/index.test.ts.snap b/x-pack/legacy/plugins/encrypted_saved_objects/__snapshots__/index.test.ts.snap deleted file mode 100644 index e3c069069bf144..00000000000000 --- a/x-pack/legacy/plugins/encrypted_saved_objects/__snapshots__/index.test.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`config schema with context {"dist":false} produces correct config 1`] = ` -Object { - "enabled": true, - "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -} -`; - -exports[`config schema with context {"dist":true} produces correct config 1`] = ` -Object { - "enabled": true, -} -`; diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/index.test.ts b/x-pack/legacy/plugins/encrypted_saved_objects/index.test.ts deleted file mode 100644 index 26c0166186c4c5..00000000000000 --- a/x-pack/legacy/plugins/encrypted_saved_objects/index.test.ts +++ /dev/null @@ -1,17 +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 { encryptedSavedObjects } from './index'; -import { getConfigSchema } from '../../../test_utils'; - -const describeWithContext = describe.each([[{ dist: false }], [{ dist: true }]]); - -describeWithContext('config schema with context %j', context => { - it('produces correct config', async () => { - const schema = await getConfigSchema(encryptedSavedObjects); - await expect(schema.validate({}, { context })).resolves.toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/index.ts b/x-pack/legacy/plugins/encrypted_saved_objects/index.ts index a5f28631f3d529..3969959d03cffd 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/index.ts +++ b/x-pack/legacy/plugins/encrypted_saved_objects/index.ts @@ -5,62 +5,41 @@ */ import { Root } from 'joi'; -import { Legacy, Server } from 'kibana'; - +import { Legacy } from 'kibana'; +import { PluginSetupContract } from '../../../plugins/encrypted_saved_objects/server'; // @ts-ignore import { AuditLogger } from '../../server/lib/audit_logger'; -import { CONFIG_PREFIX, PLUGIN_ID, Plugin } from './server/plugin'; - -/** - * Public interface of the security plugin for the legacy plugin system. - */ -export type EncryptedSavedObjectsPlugin = ReturnType; - -export const encryptedSavedObjects = (kibana: any) => +export const encryptedSavedObjects = (kibana: { + Plugin: new (options: Legacy.PluginSpecOptions & { configPrefix?: string }) => unknown; +}) => new kibana.Plugin({ - id: PLUGIN_ID, - configPrefix: CONFIG_PREFIX, - require: ['kibana', 'elasticsearch', 'xpack_main'], - - config(Joi: Root) { - return Joi.object({ - enabled: Joi.boolean().default(true), - encryptionKey: Joi.when(Joi.ref('$dist'), { - is: true, - then: Joi.string().min(32), - otherwise: Joi.string() - .min(32) - .default('a'.repeat(32)), - }), - }).default(); - }, - - async init(server: Legacy.Server) { - const loggerFacade = { - fatal: (errorOrMessage: string | Error) => server.log(['fatal', PLUGIN_ID], errorOrMessage), - trace: (message: string) => server.log(['debug', PLUGIN_ID], message), - error: (message: string) => server.log(['error', PLUGIN_ID], message), - warn: (message: string) => server.log(['warning', PLUGIN_ID], message), - debug: (message: string) => server.log(['debug', PLUGIN_ID], message), - info: (message: string) => server.log(['info', PLUGIN_ID], message), - } as Server.Logger; - - const config = server.config(); - const encryptedSavedObjectsSetup = new Plugin(loggerFacade).setup( - { - config: { - encryptionKey: config.get(`${CONFIG_PREFIX}.encryptionKey`), - }, - savedObjects: server.savedObjects, - elasticsearch: server.plugins.elasticsearch, - }, - { audit: new AuditLogger(server, PLUGIN_ID, config, server.plugins.xpack_main.info) } - ); - - // Re-expose plugin setup contract through legacy mechanism. - for (const [setupMethodName, setupMethod] of Object.entries(encryptedSavedObjectsSetup)) { - server.expose(setupMethodName, setupMethod); + id: 'encryptedSavedObjects', + configPrefix: 'xpack.encryptedSavedObjects', + require: ['xpack_main'], + + // Some legacy plugins still use `enabled` config key, so we keep it here, but the rest of the + // keys is handled by the New Platform plugin. + config: (Joi: Root) => + Joi.object({ enabled: Joi.boolean().default(true) }) + .unknown(true) + .default(), + + init(server: Legacy.Server) { + const encryptedSavedObjectsPlugin = (server.newPlatform.setup.plugins + .encryptedSavedObjects as unknown) as PluginSetupContract; + if (!encryptedSavedObjectsPlugin) { + throw new Error('New Platform XPack EncryptedSavedObjects plugin is not available.'); } + + encryptedSavedObjectsPlugin.__legacyCompat.registerLegacyAPI({ + savedObjects: server.savedObjects, + auditLogger: new AuditLogger( + server, + 'encryptedSavedObjects', + server.config(), + server.plugins.xpack_main.info + ), + }); }, }); diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.mock.ts b/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.mock.ts deleted file mode 100644 index 235d64cc348c91..00000000000000 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.mock.ts +++ /dev/null @@ -1,60 +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 { - EncryptedSavedObjectsService, - EncryptedSavedObjectTypeRegistration, - SavedObjectDescriptor, -} from './encrypted_saved_objects_service'; - -export function createEncryptedSavedObjectsServiceMock( - registrations: EncryptedSavedObjectTypeRegistration[] = [] -) { - const mock: jest.Mocked = new (jest.requireMock( - './encrypted_saved_objects_service' - )).EncryptedSavedObjectsService(); - - function processAttributes>( - descriptor: Pick, - attrs: T, - action: (attrs: T, attrName: string) => void - ) { - const registration = registrations.find(r => r.type === descriptor.type); - if (!registration) { - return attrs; - } - - const clonedAttrs = { ...attrs }; - for (const attrName of registration.attributesToEncrypt) { - if (attrName in clonedAttrs) { - action(clonedAttrs, attrName); - } - } - return clonedAttrs; - } - - mock.isRegistered.mockImplementation(type => registrations.findIndex(r => r.type === type) >= 0); - mock.encryptAttributes.mockImplementation(async (descriptor, attrs) => - processAttributes( - descriptor, - attrs, - (clonedAttrs, attrName) => (clonedAttrs[attrName] = `*${clonedAttrs[attrName]}*`) - ) - ); - mock.decryptAttributes.mockImplementation(async (descriptor, attrs) => - processAttributes( - descriptor, - attrs, - (clonedAttrs, attrName) => - (clonedAttrs[attrName] = (clonedAttrs[attrName] as string).slice(1, -1)) - ) - ); - mock.stripEncryptedAttributes.mockImplementation((type, attrs) => - processAttributes({ type }, attrs, (clonedAttrs, attrName) => delete clonedAttrs[attrName]) - ); - - return mock; -} diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.mock.ts b/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.mock.ts deleted file mode 100644 index 7c6e37c7e5d4cf..00000000000000 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.mock.ts +++ /dev/null @@ -1,21 +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 { Plugin } from './plugin'; -type EncryptedSavedObjectsPlugin = ReturnType; - -const createEncryptedSavedObjectsMock = () => { - const mocked: jest.Mocked = { - isEncryptionError: jest.fn(), - registerType: jest.fn(), - getDecryptedAsInternalUser: jest.fn(), - }; - return mocked; -}; - -export const encryptedSavedObjectsMock = { - create: createEncryptedSavedObjectsMock, -}; diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.ts deleted file mode 100644 index ad38ca0f7a881c..00000000000000 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/plugin.ts +++ /dev/null @@ -1,92 +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 crypto from 'crypto'; -import { Legacy, Server } from 'kibana'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SavedObjectsRepository } from 'src/core/server/saved_objects/service'; -import { SavedObjectsBaseOptions, SavedObject, SavedObjectAttributes } from 'src/core/server'; -import { - EncryptedSavedObjectsService, - EncryptedSavedObjectTypeRegistration, - EncryptionError, - EncryptedSavedObjectsAuditLogger, - EncryptedSavedObjectsClientWrapper, -} from './lib'; - -export const PLUGIN_ID = 'encrypted_saved_objects'; -export const CONFIG_PREFIX = `xpack.${PLUGIN_ID}`; - -interface CoreSetup { - config: { encryptionKey?: string }; - elasticsearch: Legacy.Plugins.elasticsearch.Plugin; - savedObjects: Legacy.SavedObjectsService; -} - -interface PluginsSetup { - audit: unknown; -} - -export class Plugin { - constructor(private readonly log: Server.Logger) {} - - public setup(core: CoreSetup, plugins: PluginsSetup) { - let encryptionKey = core.config.encryptionKey; - if (encryptionKey == null) { - this.log.warn( - `Generating a random key for ${CONFIG_PREFIX}.encryptionKey. To be able ` + - 'to decrypt encrypted saved objects attributes after restart, please set ' + - `${CONFIG_PREFIX}.encryptionKey in kibana.yml` - ); - - encryptionKey = crypto.randomBytes(16).toString('hex'); - } - - const service = Object.freeze( - new EncryptedSavedObjectsService( - encryptionKey, - core.savedObjects.types, - this.log, - new EncryptedSavedObjectsAuditLogger(plugins.audit) - ) - ); - - // Register custom saved object client that will encrypt, decrypt and strip saved object - // attributes where appropriate for any saved object repository request. We choose max possible - // priority for this wrapper to allow all other wrappers to set proper `namespace` for the Saved - // Object (e.g. wrapper registered by the Spaces plugin) before we encrypt attributes since - // `namespace` is included into AAD. - core.savedObjects.addScopedSavedObjectsClientWrapperFactory( - Number.MAX_SAFE_INTEGER, - 'encrypted_saved_objects', - ({ client: baseClient }) => new EncryptedSavedObjectsClientWrapper({ baseClient, service }) - ); - - const internalRepository: SavedObjectsRepository = core.savedObjects.getSavedObjectsRepository( - core.elasticsearch.getCluster('admin').callWithInternalUser - ); - - return { - isEncryptionError: (error: Error) => error instanceof EncryptionError, - registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => - service.registerType(typeRegistration), - getDecryptedAsInternalUser: async ( - type: string, - id: string, - options?: SavedObjectsBaseOptions - ): Promise> => { - const savedObject = await internalRepository.get(type, id, options); - return { - ...savedObject, - attributes: await service.decryptAttributes( - { type, id, namespace: options && options.namespace }, - savedObject.attributes - ), - }; - }, - }; - } -} diff --git a/x-pack/legacy/plugins/file_upload/public/kibana_services.js b/x-pack/legacy/plugins/file_upload/public/kibana_services.js index 8dd2923752b991..10a6ae7179bc21 100644 --- a/x-pack/legacy/plugins/file_upload/public/kibana_services.js +++ b/x-pack/legacy/plugins/file_upload/public/kibana_services.js @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { uiModules } from 'ui/modules'; +import { start as data } from '../../../../../src/legacy/core_plugins/data/public/legacy'; -export let indexPatternService; - -uiModules.get('app/file_upload').run(($injector) => { - indexPatternService = $injector.get('indexPatterns'); -}); +export const indexPatternService = data.indexPatterns.indexPatterns; diff --git a/x-pack/legacy/plugins/file_upload/public/util/indexing_service.test.js b/x-pack/legacy/plugins/file_upload/public/util/indexing_service.test.js index 47582cb47a2aa7..d6ca3d3180d6e1 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/indexing_service.test.js +++ b/x-pack/legacy/plugins/file_upload/public/util/indexing_service.test.js @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +// Not all index pattern dependencies are avab. in jest context, +// prevent unrelated import errors by mocking kibana services +jest.mock('../kibana_services', () => {}); import { checkIndexPatternValid } from './indexing_service'; describe('indexing_service', () => { diff --git a/x-pack/legacy/plugins/file_upload/public/util/size_limited_chunking.js b/x-pack/legacy/plugins/file_upload/public/util/size_limited_chunking.js index 4c49772b9ddce2..387060279ca0dd 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/size_limited_chunking.js +++ b/x-pack/legacy/plugins/file_upload/public/util/size_limited_chunking.js @@ -5,16 +5,23 @@ */ import { MAX_BYTES } from '../../common/constants/file_import'; +// MAX_BYTES is a good guideline for splitting up posts, but this logic +// occasionally sizes chunks so closely to the limit, that the remaining content +// of a post (besides features) tips it over the max. Adding a 2MB buffer +// to ensure this doesn't happen +const CHUNK_BUFFER = 2097152; + // Add data elements to chunk until limit is met -export function sizeLimitedChunking(dataArr, maxChunkSize = MAX_BYTES) { +export function sizeLimitedChunking(dataArr, maxByteSize = MAX_BYTES - CHUNK_BUFFER) { let chunkSize = 0; + return dataArr.reduce((accu, el) => { const featureByteSize = ( new Blob([JSON.stringify(el)], { type: 'application/json' }) ).size; - if (featureByteSize > maxChunkSize) { - throw `Some features exceed maximum chunk size of ${maxChunkSize}`; - } else if (chunkSize + featureByteSize < maxChunkSize) { + if (featureByteSize > maxByteSize) { + throw `Some features exceed maximum chunk size of ${maxByteSize}`; + } else if (chunkSize + featureByteSize < maxByteSize) { const lastChunkRef = accu.length - 1; chunkSize += featureByteSize; accu[lastChunkRef].push(el); diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/legacy/plugins/graph/public/app.js index ab83815457981b..fd7a292762d596 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/legacy/plugins/graph/public/app.js @@ -11,9 +11,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import { isColorDark, hexToRgb } from '@elastic/eui'; -import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; -import { formatAngularHttpError } from 'ui/notify/lib'; import { addAppRedirectMessageToUrl } from 'ui/notify'; import appTemplate from './angular/templates/index.html'; @@ -39,6 +37,7 @@ import { datasourceSelector, hasFieldsSelector } from './state_management'; +import { formatHttpError } from './helpers/format_http_error'; export function initGraphApp(angularModule, deps) { const { @@ -56,7 +55,6 @@ export function initGraphApp(angularModule, deps) { savedObjectRegistry, capabilities, coreStart, - $http, Storage, canEditDrillDownUrls, graphSavePolicy, @@ -208,31 +206,35 @@ export function initGraphApp(angularModule, deps) { async function handleHttpError(error) { checkLicense(kbnBaseUrl); - toastNotifications.addDanger(formatAngularHttpError(error)); + toastNotifications.addDanger(formatHttpError(error)); } // Replacement function for graphClientWorkspace's comms so // that it works with Kibana. function callNodeProxy(indexName, query, responseHandler) { const request = { - index: indexName, - query: query + body: JSON.stringify({ + index: indexName, + query: query + }) }; $scope.loading = true; - return $http.post('../api/graph/graphExplore', request) - .then(function (resp) { - if (resp.data.resp.timed_out) { + return coreStart.http.post('../api/graph/graphExplore', request) + .then(function (data) { + const response = data.resp; + if (response.timed_out) { toastNotifications.addWarning( i18n.translate('xpack.graph.exploreGraph.timedOutWarningText', { defaultMessage: 'Exploration timed out', }) ); } - responseHandler(resp.data.resp); + responseHandler(response); }) .catch(handleHttpError) .finally(() => { $scope.loading = false; + $scope.$digest(); }); } @@ -240,17 +242,21 @@ export function initGraphApp(angularModule, deps) { //Helper function for the graphClientWorkspace to perform a query const callSearchNodeProxy = function (indexName, query, responseHandler) { const request = { - index: indexName, - body: query + body: JSON.stringify({ + index: indexName, + body: query + }) }; $scope.loading = true; - $http.post('../api/graph/searchProxy', request) - .then(function (resp) { - responseHandler(resp.data.resp); + coreStart.http.post('../api/graph/searchProxy', request) + .then(function (data) { + const response = data.resp; + responseHandler(response); }) .catch(handleHttpError) .finally(() => { $scope.loading = false; + $scope.$digest(); }); }; diff --git a/x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts b/x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts new file mode 100644 index 00000000000000..5e0ac1e1ce56f0 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { IHttpFetchError } from 'kibana/public'; + +export function formatHttpError(error: IHttpFetchError) { + if (!error.response) { + return i18n.translate('xpack.graph.fatalError.unavailableServerErrorMessage', { + defaultMessage: + 'An HTTP request has failed to connect. ' + + 'Please check if the Kibana server is running and that your browser has a working connection, ' + + 'or contact your system administrator.', + }); + } + return i18n.translate('xpack.graph.fatalError.errorStatusMessage', { + defaultMessage: 'Error {errStatus} {errStatusText}: {errMessage}', + values: { + errStatus: error.body.status, + errStatusText: error.body.statusText, + errMessage: error.body.message, + }, + }); +} diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts index 5e500367ccdc52..48420d403653f1 100644 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ b/x-pack/legacy/plugins/graph/public/index.ts @@ -36,7 +36,6 @@ async function getAngularInjectedDependencies(): Promise('Private'); return { - $http: injector.get('$http'), savedObjectRegistry: Private(SavedObjectRegistryProvider), kbnBaseUrl: injector.get('kbnBaseUrl'), savedGraphWorkspaces: Private(SavedWorkspacesProvider), diff --git a/x-pack/legacy/plugins/graph/public/render_app.ts b/x-pack/legacy/plugins/graph/public/render_app.ts index 8625e20ab9c52d..a8a86f4d1f850e 100644 --- a/x-pack/legacy/plugins/graph/public/render_app.ts +++ b/x-pack/legacy/plugins/graph/public/render_app.ts @@ -63,10 +63,6 @@ export interface GraphDependencies extends LegacyAngularInjectedDependencies { * These dependencies have to be migrated to their NP counterparts. */ export interface LegacyAngularInjectedDependencies { - /** - * angular $http service - */ - $http: any; /** * Instance of SavedObjectRegistryProvider */ diff --git a/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx b/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx index 3c2c3d3bd05d06..7cb86f6e4d0eca 100644 --- a/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx +++ b/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx @@ -108,7 +108,7 @@ export const toMetricOpt = (metric: InfraSnapshotMetricType) => { case InfraSnapshotMetricType.tx: return { text: ToolbarTranslations.OutboundTraffic, - value: InfraSnapshotMetricType.rx, + value: InfraSnapshotMetricType.tx, }; case InfraSnapshotMetricType.logRate: return { diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_results.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_results.tsx index 8bd9f1074ac54d..81a80fb565a4b4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_results.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_results.tsx @@ -8,6 +8,23 @@ import createContainer from 'constate'; import { useMemo, useEffect } from 'react'; import { useLogEntryRate } from './log_entry_rate'; +import { GetLogEntryRateSuccessResponsePayload } from '../../../../common/http_api/log_analysis'; + +type PartitionBucket = { + startTime: number; +} & GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets'][0]['partitions'][0]; + +type PartitionRecord = Record< + string, + { buckets: PartitionBucket[]; topAnomalyScore: number; totalNumberOfLogEntries: number } +>; + +export interface LogRateResults { + bucketDuration: number; + totalNumberOfLogEntries: number; + histogramBuckets: GetLogEntryRateSuccessResponsePayload['data']['histogramBuckets']; + partitionBuckets: PartitionRecord; +} export const useLogAnalysisResults = ({ sourceId, @@ -35,10 +52,66 @@ export const useLogAnalysisResults = ({ getLogEntryRate(); }, [sourceId, startTime, endTime, bucketDuration, lastRequestTime]); + const logRateResults: LogRateResults | null = useMemo(() => { + if (logEntryRate) { + return { + bucketDuration: logEntryRate.bucketDuration, + totalNumberOfLogEntries: logEntryRate.totalNumberOfLogEntries, + histogramBuckets: logEntryRate.histogramBuckets, + partitionBuckets: formatLogEntryRateResultsByPartition(logEntryRate), + }; + } else { + return null; + } + }, [logEntryRate]); + return { isLoading, - logEntryRate, + logRateResults, }; }; export const LogAnalysisResults = createContainer(useLogAnalysisResults); + +const formatLogEntryRateResultsByPartition = ( + results: GetLogEntryRateSuccessResponsePayload['data'] +): PartitionRecord => { + const partitionedBuckets = results.histogramBuckets.reduce< + Record + >((partitionResults, bucket) => { + return bucket.partitions.reduce>( + (_partitionResults, partition) => { + return { + ..._partitionResults, + [partition.partitionId]: { + buckets: _partitionResults[partition.partitionId] + ? [ + ..._partitionResults[partition.partitionId].buckets, + { startTime: bucket.startTime, ...partition }, + ] + : [{ startTime: bucket.startTime, ...partition }], + }, + }; + }, + partitionResults + ); + }, {}); + + const resultsByPartition: PartitionRecord = {}; + + Object.entries(partitionedBuckets).map(([key, value]) => { + const anomalyScores = value.buckets.reduce((scores: number[], bucket) => { + return [...scores, bucket.maximumAnomalyScore]; + }, []); + const totalNumberOfLogEntries = value.buckets.reduce((total, bucket) => { + return (total += bucket.numberOfLogEntries); + }, 0); + resultsByPartition[key] = { + topAnomalyScore: Math.max(...anomalyScores), + totalNumberOfLogEntries, + buckets: value.buckets, + }; + }); + + return resultsByPartition; +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx b/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx index cd9ce35a32b687..86beb28a7f3ca1 100644 --- a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx @@ -106,6 +106,13 @@ export const withWaffleViewState = connect( ), }) ); + } else { + dispatch( + waffleFilterActions.applyWaffleFilterQuery({ + query: null, + serializedQuery: null, + }) + ); } }, }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index e740689da50f62..7fa9ff3c93db72 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -82,15 +82,15 @@ export const AnalysisResultsContent = ({ return roundedResult < bucketSpan ? bucketSpan : roundedResult; }, [queryTimeRange.value.startTime, queryTimeRange.value.endTime]); - const { isLoading, logEntryRate } = useLogAnalysisResults({ + const { isLoading, logRateResults } = useLogAnalysisResults({ sourceId, startTime: queryTimeRange.value.startTime, endTime: queryTimeRange.value.endTime, bucketDuration, lastRequestTime: queryTimeRange.lastChangedTime, }); - const hasResults = useMemo(() => logEntryRate && logEntryRate.histogramBuckets.length > 0, [ - logEntryRate, + const hasResults = useMemo(() => logRateResults && logRateResults.histogramBuckets.length > 0, [ + logRateResults, ]); const handleQueryTimeRangeChange = useCallback( @@ -168,7 +168,7 @@ export const AnalysisResultsContent = ({ - {logEntryRate ? ( + {logRateResults ? ( - {numeral(logEntryRate.totalNumberOfLogEntries).format('0.00a')} + {numeral(logRateResults.totalNumberOfLogEntries).format('0.00a')} ), @@ -210,7 +210,7 @@ export const AnalysisResultsContent = ({ {isFirstUse && !hasResults ? : null} @@ -223,7 +223,7 @@ export const AnalysisResultsContent = ({ jobStatus={jobStatus['log-entry-rate']} viewSetupForReconfiguration={viewSetupForReconfiguration} viewSetupForUpdate={viewSetupForUpdate} - results={logEntryRate} + results={logRateResults} setTimeRange={handleChartTimeRangeChange} setupStatus={setupStatus} timeRange={queryTimeRange.value} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx index 191e3cb7d7bfd3..317bd6ec5e925a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx @@ -25,7 +25,7 @@ export const AnalyzeInMlButton: React.FunctionComponent<{ defaultMessage="Analyze in ML" /> ); - return partition ? ( + return typeof partition === 'string' ? ( void; timeRange: TimeRange; jobId: string; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index c340de4ad3848b..26f44519312e5f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -18,7 +18,7 @@ import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import euiStyled from '../../../../../../../../common/eui_styled_components'; -import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api'; +import { LogRateResults } from '../../../../../containers/logs/log_analysis/log_analysis_results'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { JobStatus, SetupStatus } from '../../../../../../common/log_analysis'; import { @@ -36,7 +36,7 @@ import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; jobStatus: JobStatus; - results: GetLogEntryRateSuccessResponsePayload['data'] | null; + results: LogRateResults | null; setTimeRange: (timeRange: TimeRange) => void; setupStatus: SetupStatus; timeRange: TimeRange; @@ -195,7 +195,7 @@ const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', }); interface ParsedAnnotationDetails { - anomalyScoresByPartition: Array<{ partitionId: string; maximumAnomalyScore: number }>; + anomalyScoresByPartition: Array<{ partitionName: string; maximumAnomalyScore: number }>; } const overallAnomalyScoreLabel = i18n.translate( @@ -213,11 +213,11 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai {overallAnomalyScoreLabel}
    - {parsedDetails.anomalyScoresByPartition.map(({ partitionId, maximumAnomalyScore }) => { + {parsedDetails.anomalyScoresByPartition.map(({ partitionName, maximumAnomalyScore }) => { return ( -
  • +
  • - {`${partitionId}: `} + {`${partitionName}: `} {maximumAnomalyScore}
  • diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 88ffcae6e9dea1..ebf31d8320df55 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -9,9 +9,9 @@ import { EuiBasicTable, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; -import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import { LogRateResults } from '../../../../../containers/logs/log_analysis/log_analysis_results'; import { AnomaliesTableExpandedRow } from './expanded_row'; -import { getTopAnomalyScoresByPartition, formatAnomalyScore } from '../helpers/data_formatters'; +import { formatAnomalyScore, getFriendlyNameForPartitionId } from '../helpers/data_formatters'; interface TableItem { id: string; @@ -49,17 +49,20 @@ const maxAnomalyScoreColumnName = i18n.translate( ); export const AnomaliesTable: React.FunctionComponent<{ - results: GetLogEntryRateSuccessResponsePayload['data']; + results: LogRateResults; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; jobId: string; }> = ({ results, timeRange, setTimeRange, jobId }) => { const tableItems: TableItem[] = useMemo(() => { - return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { + return Object.entries(results.partitionBuckets).map(([key, value]) => { return { - id: key || 'unknown', // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap - partition: key || 'unknown', - topAnomalyScore: formatAnomalyScore(value), + // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap, so we have to use the friendly name here + id: getFriendlyNameForPartitionId(key), + // The real ID + partitionId: key, + partition: getFriendlyNameForPartitionId(key), + topAnomalyScore: formatAnomalyScore(value.topAnomalyScore), }; }); }, [results]); @@ -108,7 +111,7 @@ export const AnomaliesTable: React.FunctionComponent<{ ...itemIdToExpandedRowMap, [item.id]: ( ; @@ -17,15 +17,13 @@ const ML_SEVERITY_SCORES: MLSeverityScores = { critical: 75, }; -export const getLogEntryRatePartitionedSeries = ( - results: GetLogEntryRateSuccessResponsePayload['data'] -) => { +export const getLogEntryRatePartitionedSeries = (results: LogRateResults) => { return results.histogramBuckets.reduce>( (buckets, bucket) => { return [ ...buckets, ...bucket.partitions.map(partition => ({ - group: partition.partitionId === '' ? 'unknown' : partition.partitionId, + group: getFriendlyNameForPartitionId(partition.partitionId), time: bucket.startTime, value: partition.averageActualLogEntryRate, })), @@ -35,9 +33,7 @@ export const getLogEntryRatePartitionedSeries = ( ); }; -export const getLogEntryRateCombinedSeries = ( - results: GetLogEntryRateSuccessResponsePayload['data'] -) => { +export const getLogEntryRateCombinedSeries = (results: LogRateResults) => { return results.histogramBuckets.reduce>( (buckets, bucket) => { return [ @@ -54,69 +50,27 @@ export const getLogEntryRateCombinedSeries = ( ); }; -export const getLogEntryRateSeriesForPartition = ( - results: GetLogEntryRateSuccessResponsePayload['data'], - partitionId: string -) => { - return results.histogramBuckets.reduce>( - (buckets, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return ( - partition.partitionId === partitionId || - (partition.partitionId === '' && partitionId === 'unknown') - ); - }); - if (!partitionResults) { - return buckets; - } - return [ - ...buckets, - { - time: bucket.startTime, - value: partitionResults.averageActualLogEntryRate, - }, - ]; - }, - [] - ); -}; - -export const getTopAnomalyScoresByPartition = ( - results: GetLogEntryRateSuccessResponsePayload['data'] -) => { - return results.histogramBuckets.reduce>((topScores, bucket) => { - bucket.partitions.forEach(partition => { - if (partition.maximumAnomalyScore > 0) { - topScores = { - ...topScores, - [partition.partitionId]: - !topScores[partition.partitionId] || - partition.maximumAnomalyScore > topScores[partition.partitionId] - ? partition.maximumAnomalyScore - : topScores[partition.partitionId], - }; - } - }); - return topScores; - }, {}); +export const getLogEntryRateSeriesForPartition = (results: LogRateResults, partitionId: string) => { + return results.partitionBuckets[partitionId].buckets.reduce< + Array<{ time: number; value: number }> + >((buckets, bucket) => { + return [ + ...buckets, + { + time: bucket.startTime, + value: bucket.averageActualLogEntryRate, + }, + ]; + }, []); }; -export const getAnnotationsForPartition = ( - results: GetLogEntryRateSuccessResponsePayload['data'], - partitionId: string -) => { - return results.histogramBuckets.reduce>( +export const getAnnotationsForPartition = (results: LogRateResults, partitionId: string) => { + return results.partitionBuckets[partitionId].buckets.reduce< + Record + >( (annotatedBucketsBySeverity, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return ( - partition.partitionId === partitionId || - (partition.partitionId === '' && partitionId === 'unknown') - ); - }); - const severityCategory = partitionResults - ? getSeverityCategoryForScore(partitionResults.maximumAnomalyScore) - : null; - if (!partitionResults || !partitionResults.maximumAnomalyScore || !severityCategory) { + const severityCategory = getSeverityCategoryForScore(bucket.maximumAnomalyScore); + if (!severityCategory) { return annotatedBucketsBySeverity; } @@ -134,7 +88,7 @@ export const getAnnotationsForPartition = ( { defaultMessage: 'Max anomaly score: {maxAnomalyScore}', values: { - maxAnomalyScore: formatAnomalyScore(partitionResults.maximumAnomalyScore), + maxAnomalyScore: formatAnomalyScore(bucket.maximumAnomalyScore), }, } ), @@ -152,29 +106,17 @@ export const getAnnotationsForPartition = ( }; export const getTotalNumberOfLogEntriesForPartition = ( - results: GetLogEntryRateSuccessResponsePayload['data'], + results: LogRateResults, partitionId: string ) => { - return results.histogramBuckets.reduce((sumPartitionNumberOfLogEntries, bucket) => { - const partitionResults = bucket.partitions.find(partition => { - return ( - partition.partitionId === partitionId || - (partition.partitionId === '' && partitionId === 'unknown') - ); - }); - if (!partitionResults || !partitionResults.numberOfLogEntries) { - return sumPartitionNumberOfLogEntries; - } else { - return (sumPartitionNumberOfLogEntries += partitionResults.numberOfLogEntries); - } - }, 0); + return results.partitionBuckets[partitionId].totalNumberOfLogEntries; }; -export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayload['data']) => { +export const getAnnotationsForAll = (results: LogRateResults) => { return results.histogramBuckets.reduce>( (annotatedBucketsBySeverity, bucket) => { const maxAnomalyScoresByPartition = bucket.partitions.reduce< - Array<{ partitionId: string; maximumAnomalyScore: number }> + Array<{ partitionName: string; maximumAnomalyScore: number }> >((bucketMaxAnomalyScoresByPartition, partition) => { if (!getSeverityCategoryForScore(partition.maximumAnomalyScore)) { return bucketMaxAnomalyScoresByPartition; @@ -182,7 +124,7 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl return [ ...bucketMaxAnomalyScoresByPartition, { - partitionId: partition.partitionId ? partition.partitionId : 'unknown', + partitionName: getFriendlyNameForPartitionId(partition.partitionId), maximumAnomalyScore: formatAnomalyScore(partition.maximumAnomalyScore), }, ]; @@ -227,16 +169,14 @@ export const getAnnotationsForAll = (results: GetLogEntryRateSuccessResponsePayl ); }; -export const getTopAnomalyScoreAcrossAllPartitions = ( - results: GetLogEntryRateSuccessResponsePayload['data'] -) => { - const allMaxScores = results.histogramBuckets.reduce((scores, bucket) => { - const bucketMaxScores = bucket.partitions.reduce((bucketScores, partition) => { - return [...bucketScores, partition.maximumAnomalyScore]; - }, []); - return [...scores, ...bucketMaxScores]; - }, []); - return Math.max(...allMaxScores); +export const getTopAnomalyScoreAcrossAllPartitions = (results: LogRateResults) => { + const allTopScores = Object.values(results.partitionBuckets).reduce( + (scores: number[], partition) => { + return [...scores, partition.topAnomalyScore]; + }, + [] + ); + return Math.max(...allTopScores); }; const getSeverityCategoryForScore = (score: number): MLSeverityScoreCategories | undefined => { @@ -257,3 +197,7 @@ const getSeverityCategoryForScore = (score: number): MLSeverityScoreCategories | export const formatAnomalyScore = (score: number) => { return Math.round(score); }; + +export const getFriendlyNameForPartitionId = (partitionId: string) => { + return partitionId !== '' ? partitionId : 'unknown'; +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx index 682eb23fa4774d..44805520f3b9ea 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx @@ -8,7 +8,7 @@ import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiTitle, EuiText } from import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; -import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; +import { LogRateResults as Results } from '../../../../../containers/logs/log_analysis/log_analysis_results'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; import { LogEntryRateBarChart } from './bar_chart'; import { getLogEntryRatePartitionedSeries } from '../helpers/data_formatters'; @@ -21,7 +21,7 @@ export const LogRateResults = ({ timeRange, }: { isLoading: boolean; - results: GetLogEntryRateSuccessResponsePayload['data'] | null; + results: Results | null; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; }) => { diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts b/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts index a34cc9659f0b7e..912ad96357334d 100644 --- a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts +++ b/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts @@ -16,8 +16,8 @@ export interface KueryFilterQuery { export type FilterQuery = KueryFilterQuery; export interface SerializedFilterQuery { - query: FilterQuery; - serializedQuery: string; + query: FilterQuery | null; + serializedQuery: string | null; } export interface WaffleFilterState { diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index a1710d67b31db4..31d622c7089a86 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { buildExistsFilter } from '@kbn/es-query'; import { App } from './app'; import { EditorFrameInstance } from '../types'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { Document, SavedObjectStore } from '../persistence'; import { mount } from 'enzyme'; +import { esFilters } from '../../../../../../src/plugins/data/public'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; const dataStartMock = dataPluginMock.createStartContract(); @@ -595,7 +595,7 @@ describe('Lens App', () => { const instance = mount(); args.data.query.filterManager.setFilters([ - buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), + esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), ]); instance.update(); @@ -603,7 +603,7 @@ describe('Lens App', () => { expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - filters: [buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], + filters: [esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], }) ); }); @@ -726,7 +726,7 @@ describe('Lens App', () => { }); args.data.query.filterManager.setFilters([ - buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), + esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), ]); instance.update(); diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index a95e0450f614c8..2815ac9ddda4e5 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -18,7 +18,6 @@ import { SavedQuery, Query, } from 'src/legacy/core_plugins/data/public'; -import { Filter } from '@kbn/es-query'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { start as navigation } from '../../../../../../src/legacy/core_plugins/navigation/public/legacy'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; @@ -26,6 +25,7 @@ import { Document, SavedObjectStore } from '../persistence'; import { EditorFrameInstance } from '../types'; import { NativeRenderer } from '../native_renderer'; import { trackUiEvent } from '../lens_ui_telemetry'; +import { esFilters } from '../../../../../../src/plugins/data/public'; interface State { isLoading: boolean; @@ -40,7 +40,7 @@ interface State { toDate: string; }; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; savedQuery?: SavedQuery; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.test.tsx index 0a540334f2cad1..1b60098fd45ad1 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.test.tsx @@ -369,7 +369,7 @@ describe('chart_switch', () => { ); }); - it('should not remove layers if the visualization is not changing', () => { + it('should not remove layers when switching between subtypes', () => { const dispatch = jest.fn(); const frame = mockFrame(['a', 'b', 'c']); const visualizations = mockVisualizations(); @@ -397,6 +397,7 @@ describe('chart_switch', () => { initialState: 'therebedragons', }) ); + expect(frame.removeLayers).not.toHaveBeenCalled(); }); it('should switch to the updated datasource state', () => { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx index 8b67646edded91..dca6b3e7616d67 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx @@ -28,6 +28,7 @@ interface VisualizationSelection { dataLoss: 'nothing' | 'layers' | 'everything' | 'columns'; datasourceId?: string; datasourceState?: unknown; + sameDatasources?: boolean; } interface Props { @@ -89,7 +90,10 @@ export function ChartSwitch(props: Props) { 'SWITCH_VISUALIZATION' ); - if (!selection.datasourceId || selection.dataLoss === 'everything') { + if ( + (!selection.datasourceId && !selection.sameDatasources) || + selection.dataLoss === 'everything' + ) { props.framePublicAPI.removeLayers(Object.keys(props.framePublicAPI.datasourceLayers)); } }; @@ -109,6 +113,7 @@ export function ChartSwitch(props: Props) { dataLoss: 'nothing', keptLayerIds: Object.keys(props.framePublicAPI.datasourceLayers), getVisualizationState: () => switchVisType(subVisualizationId, props.visualizationState), + sameDatasources: true, }; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx index 115e8cbf002c3b..a5509cdc945595 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx @@ -6,7 +6,6 @@ import React, { useMemo, memo, useContext, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { Filter } from '@kbn/es-query'; import { EuiPopover, EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; import { Query } from 'src/plugins/data/common'; import { DatasourceDataPanelProps, Datasource } from '../../../public'; @@ -14,6 +13,7 @@ import { NativeRenderer } from '../../native_renderer'; import { Action } from './state_management'; import { DragContext } from '../../drag_drop'; import { StateSetter, FramePublicAPI } from '../../types'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; interface DataPanelWrapperProps { datasourceState: unknown; @@ -24,7 +24,7 @@ interface DataPanelWrapperProps { core: DatasourceDataPanelProps['core']; query: Query; dateRange: FramePublicAPI['dateRange']; - filters: Filter[]; + filters: esFilters.Filter[]; } export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx index 8e89d8edc9f230..6d782d119525dc 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx @@ -6,7 +6,6 @@ import React, { useEffect, useReducer } from 'react'; import { CoreSetup, CoreStart } from 'src/core/public'; -import { Filter } from '@kbn/es-query'; import { Query, SavedQuery } from '../../../../../../../src/legacy/core_plugins/data/public'; import { ExpressionRenderer } from '../../../../../../../src/legacy/core_plugins/expressions/public'; import { @@ -26,6 +25,7 @@ import { Document } from '../../persistence/saved_object_store'; import { getSavedObjectFormat } from './save'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; import { generateId } from '../../id_generator'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; export interface EditorFrameProps { doc?: Document; @@ -41,7 +41,7 @@ export interface EditorFrameProps { toDate: string; }; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; savedQuery?: SavedQuery; onChange: (arg: { filterableIndexPatterns: DatasourceMetaData['filterableIndexPatterns']; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/expression_helpers.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/expression_helpers.ts index 1ddfc54cc187b1..f03b64295641bd 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/expression_helpers.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/expression_helpers.ts @@ -6,9 +6,9 @@ import { TimeRange } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; -import { Filter } from '@kbn/es-query'; import { Ast, fromExpression, ExpressionFunctionAST } from '@kbn/interpreter/common'; import { Visualization, Datasource, FramePublicAPI } from '../../types'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; export function prependDatasourceExpression( visualizationExpression: Ast | string | null, @@ -71,7 +71,7 @@ export function prependKibanaContext( }: { timeRange?: TimeRange; query?: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; } ): Ast { const parsedExpression = typeof expression === 'string' ? fromExpression(expression) : expression; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts index ef8194aa6f924b..f223a9c06a2a75 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildExistsFilter } from '@kbn/es-query'; import { getSavedObjectFormat, Props } from './save'; import { createMockDatasource, createMockVisualization } from '../mocks'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; describe('save editor frame state', () => { const mockVisualization = createMockVisualization(); @@ -37,7 +37,7 @@ describe('save editor frame state', () => { }, query: { query: '', language: 'lucene' }, dateRange: { fromDate: 'now-7d', toDate: 'now' }, - filters: [buildExistsFilter({ name: '@timestamp' }, { id: 'indexpattern' })], + filters: [esFilters.buildExistsFilter({ name: '@timestamp' }, { id: 'indexpattern' })], }, }; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx index 3b79172ea6572d..fd35ecd702d23a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx @@ -5,8 +5,6 @@ */ import React from 'react'; - -import { buildExistsFilter } from '@kbn/es-query'; import { ExpressionRendererProps } from '../../../../../../../src/legacy/core_plugins/expressions/public'; import { Visualization, FramePublicAPI, TableSuggestion } from '../../types'; import { @@ -22,6 +20,7 @@ import { ReactWrapper } from 'enzyme'; import { DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { Ast } from '@kbn/interpreter/common'; import { coreMock } from 'src/core/public/mocks'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); @@ -382,7 +381,7 @@ describe('workspace_panel', () => { instance.setProps({ framePublicAPI: { ...framePublicAPI, - filters: [buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], + filters: [esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], }, }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.test.tsx index d728457b7e3a3b..95d39409c57de3 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.test.tsx @@ -5,10 +5,9 @@ */ import { Embeddable } from './embeddable'; -import { TimeRange } from 'src/plugins/data/public'; +import { TimeRange, esFilters } from 'src/plugins/data/public'; import { Query } from 'src/legacy/core_plugins/data/public'; import { ExpressionRendererProps } from 'src/legacy/core_plugins/expressions/public'; -import { Filter } from '@kbn/es-query'; import { Document } from '../../persistence'; jest.mock('../../../../../../../src/legacy/ui/public/inspector', () => ({ @@ -63,7 +62,9 @@ describe('embeddable', () => { it('should re-render if new input is pushed', () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; - const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; + const filters: esFilters.Filter[] = [ + { meta: { alias: 'test', negate: false, disabled: false } }, + ]; const embeddable = new Embeddable( expressionRenderer, @@ -88,7 +89,9 @@ describe('embeddable', () => { it('should pass context to embeddable', () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; - const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; + const filters: esFilters.Filter[] = [ + { meta: { alias: 'test', negate: false, disabled: false } }, + ]; const embeddable = new Embeddable( expressionRenderer, @@ -112,7 +115,9 @@ describe('embeddable', () => { it('should not re-render if only change is in disabled filter', () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; - const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; + const filters: esFilters.Filter[] = [ + { meta: { alias: 'test', negate: false, disabled: true } }, + ]; const embeddable = new Embeddable( expressionRenderer, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.tsx index e815a1951bdb70..0b5d4909c8e73a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/embeddable.tsx @@ -8,10 +8,9 @@ import _ from 'lodash'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { TimeRange } from 'src/plugins/data/public'; +import { TimeRange, esFilters } from 'src/plugins/data/public'; import { Query, StaticIndexPattern } from 'src/legacy/core_plugins/data/public'; import { ExpressionRenderer } from 'src/legacy/core_plugins/expressions/public'; -import { Filter } from '@kbn/es-query'; import { Subscription } from 'rxjs'; import { Embeddable as AbstractEmbeddable, @@ -32,7 +31,7 @@ export interface LensEmbeddableConfiguration { export interface LensEmbeddableInput extends EmbeddableInput { timeRange?: TimeRange; query?: Query; - filters?: Filter[]; + filters?: esFilters.Filter[]; } export interface LensEmbeddableOutput extends EmbeddableOutput { @@ -50,7 +49,7 @@ export class Embeddable extends AbstractEmbeddable { + it('should do nothing if no time range is provided', () => { + const result = autoDate.fn( + { + type: 'kibana_context', + }, + { + aggConfigs: 'canttouchthis', + }, + {} + ); + + expect(result).toEqual('canttouchthis'); + }); + + it('should not change anything if there are no auto date histograms', () => { + const aggConfigs = JSON.stringify([ + { type: 'date_histogram', params: { interval: '35h' } }, + { type: 'count' }, + ]); + const result = autoDate.fn( + { + timeRange: { + from: 'now-10d', + to: 'now', + }, + type: 'kibana_context', + }, + { + aggConfigs, + }, + {} + ); + + expect(result).toEqual(aggConfigs); + }); + + it('should change auto date histograms', () => { + const aggConfigs = JSON.stringify([ + { type: 'date_histogram', params: { interval: 'auto' } }, + { type: 'count' }, + ]); + const result = autoDate.fn( + { + timeRange: { + from: 'now-10d', + to: 'now', + }, + type: 'kibana_context', + }, + { + aggConfigs, + }, + {} + ); + + const interval = JSON.parse(result).find( + (agg: { type: string }) => agg.type === 'date_histogram' + ).params.interval; + + expect(interval).toBeTruthy(); + expect(typeof interval).toEqual('string'); + expect(interval).not.toEqual('auto'); + }); +}); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts index 511f2b11c4794f..dec0adba98103a 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/auto_date.ts @@ -89,7 +89,7 @@ export const autoDate: ExpressionFunction< ...c, params: { ...c.params, - interval: interval.expression, + interval, }, }; }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index e7444e8a192ea5..962eece4977e01 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -35,8 +35,9 @@ import { niceTimeFormatter, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Filter, buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; +import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; import { Query } from 'src/plugins/data/common'; +import { esFilters } from '../../../../../../src/plugins/data/public'; // @ts-ignore import { fieldFormats } from '../../../../../../src/legacy/ui/public/registry/field_formats'; import { DraggedField } from './indexpattern'; @@ -55,7 +56,7 @@ export interface FieldItemProps { exists: boolean; query: Query; dateRange: DatasourceDataPanelProps['dateRange']; - filters: Filter[]; + filters: esFilters.Filter[]; hideDetails?: boolean; } diff --git a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts index 5fa7e3f0aca4ac..460cd76e8390d5 100644 --- a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts @@ -6,8 +6,8 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { SavedObjectAttributes } from 'src/core/server'; -import { Filter } from '@kbn/es-query'; import { Query } from 'src/plugins/data/common'; +import { esFilters } from '../../../../../../src/plugins/data/public'; export interface Document { id?: string; @@ -22,7 +22,7 @@ export interface Document { datasourceStates: Record; visualization: unknown; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; }; } diff --git a/x-pack/legacy/plugins/lens/public/types.ts b/x-pack/legacy/plugins/lens/public/types.ts index 2b97f193a563cb..b66bb7bee8f8ad 100644 --- a/x-pack/legacy/plugins/lens/public/types.ts +++ b/x-pack/legacy/plugins/lens/public/types.ts @@ -6,7 +6,6 @@ import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; -import { Filter } from '@kbn/es-query'; import { CoreSetup } from 'src/core/public'; import { Query } from 'src/plugins/data/common'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; @@ -14,6 +13,7 @@ import { KibanaDatatable } from '../../../../../src/legacy/core_plugins/interpre import { DragContextState } from './drag_drop'; import { Document } from './persistence'; import { DateRange } from '../common'; +import { esFilters } from '../../../../../src/plugins/data/public'; // eslint-disable-next-line export interface EditorFrameOptions {} @@ -32,7 +32,7 @@ export interface EditorFrameProps { doc?: Document; dateRange: DateRange; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; savedQuery?: SavedQuery; // Frame loader (app or embeddable) is expected to call this when it loads and updates @@ -179,7 +179,7 @@ export interface DatasourceDataPanelProps { core: Pick; query: Query; dateRange: DateRange; - filters: Filter[]; + filters: esFilters.Filter[]; } // The only way a visualization has to restrict the query building @@ -309,7 +309,7 @@ export interface FramePublicAPI { dateRange: DateRange; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; // Adds a new layer. This has a side effect of updating the datasource state addNewLayer: () => string; diff --git a/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts b/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts index b6ff1e6ac757b3..ad1af966983fb3 100644 --- a/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts @@ -11,10 +11,7 @@ import _ from 'lodash'; import { IScopedClusterClient } from 'src/core/server'; import { CoreSetup } from 'src/core/server'; import { BASE_API_URL } from '../../common'; -import { - FieldDescriptor, - IndexPatternsService, -} from '../../../../../../src/legacy/server/index_patterns/service'; +import { FieldDescriptor, IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; /** * The number of docs to sample to determine field empty status. @@ -42,11 +39,11 @@ export async function existingFieldsRoute(setup: CoreSetup) { async (context, req, res) => { const { indexPatternTitle } = req.params; const requestClient = context.core.elasticsearch.dataClient; - const indexPatternsService = new IndexPatternsService(requestClient.callAsCurrentUser); + const indexPatternsFetcher = new IndexPatternsFetcher(requestClient.callAsCurrentUser); const { fromDate, toDate, timeFieldName } = req.query; try { - const fields = await indexPatternsService.getFieldsForWildcard({ + const fields = await indexPatternsFetcher.getFieldsForWildcard({ pattern: indexPatternTitle, // TODO: Pull this from kibana advanced settings metaFields: ['_source', '_id', '_type', '_index', '_score'], diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index a8cc328e532e51..739e98beec10f1 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -16,7 +16,7 @@ import { watchStatusAndLicenseToInitialize } from '../../server/lib/watch_status_and_license_to_initialize'; import { initTelemetryCollection } from './server/maps_telemetry'; import { i18n } from '@kbn/i18n'; -import { APP_ID, APP_ICON, createMapPath } from './common/constants'; +import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common/constants'; import { getAppTitle } from './common/i18n_getters'; import _ from 'lodash'; @@ -71,7 +71,7 @@ export function maps(kibana) { } }, savedObjectsManagement: { - 'map': { + [MAP_SAVED_OBJECT_TYPE]: { icon: APP_ICON, defaultSearchField: 'title', isImportableAndExportable: true, @@ -112,18 +112,18 @@ export function maps(kibana) { let routesInitialized = false; xpackMainPlugin.registerFeature({ - id: 'maps', + id: APP_ID, name: i18n.translate('xpack.maps.featureRegistry.mapsFeatureName', { defaultMessage: 'Maps', }), icon: APP_ICON, - navLinkId: 'maps', + navLinkId: APP_ID, app: [APP_ID, 'kibana'], - catalogue: ['maps'], + catalogue: [APP_ID], privileges: { all: { savedObject: { - all: ['map', 'query'], + all: [MAP_SAVED_OBJECT_TYPE, 'query'], read: ['index-pattern'] }, ui: ['save', 'show', 'saveQuery'], @@ -131,7 +131,7 @@ export function maps(kibana) { read: { savedObject: { all: [], - read: ['map', 'index-pattern', 'query'] + read: [MAP_SAVED_OBJECT_TYPE, 'index-pattern', 'query'] }, ui: ['show'], }, @@ -158,7 +158,7 @@ export function maps(kibana) { { path: createMapPath('2c9c1f60-1909-11e9-919b-ffe5949a18d2'), label: sampleDataLinkLabel, - icon: 'gisApp' + icon: APP_ICON } ]); server.replacePanelInSampleDatasetDashboard({ @@ -177,7 +177,7 @@ export function maps(kibana) { { path: createMapPath('5dd88580-1906-11e9-919b-ffe5949a18d2'), label: sampleDataLinkLabel, - icon: 'gisApp' + icon: APP_ICON } ]); server.replacePanelInSampleDatasetDashboard({ @@ -185,7 +185,7 @@ export function maps(kibana) { dashboardId: '7adfa750-4c81-11e8-b3d7-01146121b73d', oldEmbeddableId: '334084f0-52fd-11e8-a160-89cc2ad9e8e2', embeddableId: '5dd88580-1906-11e9-919b-ffe5949a18d2', - embeddableType: 'map', + embeddableType: MAP_SAVED_OBJECT_TYPE, embeddableConfig: { isLayerTOCOpen: true }, @@ -196,7 +196,7 @@ export function maps(kibana) { { path: createMapPath('de71f4f0-1902-11e9-919b-ffe5949a18d2'), label: sampleDataLinkLabel, - icon: 'gisApp' + icon: APP_ICON } ]); server.replacePanelInSampleDatasetDashboard({ @@ -204,13 +204,13 @@ export function maps(kibana) { dashboardId: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b', oldEmbeddableId: '06cf9c40-9ee8-11e7-8711-e7a007dcef99', embeddableId: 'de71f4f0-1902-11e9-919b-ffe5949a18d2', - embeddableType: 'map', + embeddableType: MAP_SAVED_OBJECT_TYPE, embeddableConfig: { isLayerTOCOpen: false }, }); - server.injectUiAppVars('maps', async () => { + server.injectUiAppVars(APP_ID, async () => { return await server.getInjectedUiAppVars('kibana'); }); } diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.js b/x-pack/legacy/plugins/maps/public/actions/map_actions.js index 509c652471ffa1..33771a355ddd5b 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.js +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.js @@ -21,10 +21,15 @@ import { FLYOUT_STATE } from '../reducers/ui'; import { cancelRequest, registerCancelCallback, - unregisterCancelCallback + unregisterCancelCallback, + getEventHandlers, } from '../reducers/non_serializable_instances'; import { updateFlyout } from '../actions/ui_actions'; -import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN } from '../../common/constants'; +import { + FEATURE_ID_PROPERTY_NAME, + LAYER_TYPE, + SOURCE_DATA_ID_ORIGIN +} from '../../common/constants'; export const SET_SELECTED_LAYER = 'SET_SELECTED_LAYER'; export const SET_TRANSIENT_LAYER = 'SET_TRANSIENT_LAYER'; @@ -456,6 +461,14 @@ export function startDataLoad(layerId, dataId, requestToken, meta = {}) { dispatch(cancelRequest(layer.getPrevRequestToken(dataId))); } + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoad) { + eventHandlers.onDataLoad({ + layerId, + dataId, + }); + } + dispatch({ meta, type: LAYER_DATA_LOAD_STARTED, @@ -480,9 +493,26 @@ export function updateSourceDataRequest(layerId, newData) { } export function endDataLoad(layerId, dataId, requestToken, data, meta) { - return async (dispatch) => { + return async (dispatch, getState) => { dispatch(unregisterCancelCallback(requestToken)); - const features = data ? data.features : []; + + const features = data && data.features ? data.features : []; + + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoadEnd) { + const layer = getLayerById(layerId, getState()); + const resultMeta = {}; + if (layer && layer.getType() === LAYER_TYPE.VECTOR) { + resultMeta.featuresCount = features.length; + } + + eventHandlers.onDataLoadEnd({ + layerId, + dataId, + resultMeta + }); + } + dispatch(cleanTooltipStateForLayer(layerId, features)); dispatch({ type: LAYER_DATA_LOAD_ENDED, @@ -503,8 +533,18 @@ export function endDataLoad(layerId, dataId, requestToken, data, meta) { } export function onDataLoadError(layerId, dataId, requestToken, errorMessage) { - return async (dispatch) => { + return async (dispatch, getState) => { dispatch(unregisterCancelCallback(requestToken)); + + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoadError) { + eventHandlers.onDataLoadError({ + layerId, + dataId, + errorMessage, + }); + } + dispatch(cleanTooltipStateForLayer(layerId)); dispatch({ type: LAYER_DATA_LOAD_ERROR, diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 594548333cb8c7..41c618d68a68e4 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -53,9 +53,9 @@ import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../common/constants'; -import { FilterStateStore } from '@kbn/es-query'; import { start as data } from '../../../../../../src/legacy/core_plugins/data/public/legacy'; import { npStart } from 'ui/new_platform'; +import { esFilters } from '../../../../../../src/plugins/data/public'; const { savedQueryService } = data.search.services; @@ -247,7 +247,7 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta function addFilters(newFilters) { newFilters.forEach(filter => { - filter.$state = FilterStateStore.APP_STATE; + filter.$state = esFilters.FilterStateStore.APP_STATE; }); $scope.updateFiltersAndDispatch([...$scope.filters, ...newFilters]); } diff --git a/x-pack/legacy/plugins/maps/public/embeddable/README.md b/x-pack/legacy/plugins/maps/public/embeddable/README.md index 329ed930ac2926..ccd7899b1c9142 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/README.md +++ b/x-pack/legacy/plugins/maps/public/embeddable/README.md @@ -58,3 +58,21 @@ const renderTooltipContent = ({ addFilters, closeTooltip, features, isLocked, lo const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent); ``` + + +#### Event handlers +``` +const eventHandlers = { + onDataLoad: (layerId: string, dataId: string) => { + // take action on data load + }, + onDataLoadEnd: (layerId: string, dataId: string, resultMeta: object) => { + // take action on data load end + }, + onDataLoadError: (layerId: string, dataId: string, errorMessage: string) => { + // take action on data load error + }, +} + +const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent, eventHandlers); +``` diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js index 18b8dba6702d4d..53931d1953ab3e 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js @@ -31,14 +31,14 @@ import { setOpenTOCDetails, } from '../actions/ui_actions'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors'; -import { getInspectorAdapters } from '../reducers/non_serializable_instances'; +import { getInspectorAdapters, setEventHandlers } from '../reducers/non_serializable_instances'; import { getMapCenter, getMapZoom } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - constructor(config, initialInput, parent, renderTooltipContent) { + constructor(config, initialInput, parent, renderTooltipContent, eventHandlers) { super( initialInput, { @@ -50,6 +50,7 @@ export class MapEmbeddable extends Embeddable { parent); this._renderTooltipContent = renderTooltipContent; + this._eventHandlers = eventHandlers; this._layerList = config.layerList; this._store = createMapStore(); @@ -98,6 +99,7 @@ export class MapEmbeddable extends Embeddable { * @param {ContainerState} containerState */ render(domNode) { + this._store.dispatch(setEventHandlers(this._eventHandlers)); this._store.dispatch(setReadOnly(true)); this._store.dispatch(disableScrollZoom()); diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js index 21c289d9eb2814..f09726039b0c31 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js @@ -123,6 +123,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { input, parent, renderTooltipContent, + eventHandlers, ) { const layerList = state && state.layerList ? state.layerList : getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); @@ -137,7 +138,8 @@ export class MapEmbeddableFactory extends EmbeddableFactory { }, input, parent, - renderTooltipContent + renderTooltipContent, + eventHandlers, ); } diff --git a/x-pack/legacy/plugins/maps/public/index_pattern_util.js b/x-pack/legacy/plugins/maps/public/index_pattern_util.js index 9a0258592a606c..da75ced2d02af1 100644 --- a/x-pack/legacy/plugins/maps/public/index_pattern_util.js +++ b/x-pack/legacy/plugins/maps/public/index_pattern_util.js @@ -28,6 +28,7 @@ export function getTermsFields(fields) { export function getSourceFields(fields) { return fields.filter(field => { // Multi fields are not stored in _source and only exist in index. - return field.subType && field.subType.multi; + const isMultiField = field.subType && field.subType.multi; + return !isMultiField; }); } diff --git a/x-pack/legacy/plugins/maps/public/index_pattern_util.test.js b/x-pack/legacy/plugins/maps/public/index_pattern_util.test.js new file mode 100644 index 00000000000000..aedff273f646b8 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/index_pattern_util.test.js @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('./kibana_services', () => ({})); + +import { getSourceFields } from './index_pattern_util'; + +describe('getSourceFields', () => { + test('Should remove multi fields from field list', () => { + const fields = [ + { + name: 'agent' + }, + { + name: 'agent.keyword', + subType: { + multi: { + parent: 'agent' + } + } + } + ]; + const sourceFields = getSourceFields(fields); + expect(sourceFields).toEqual([ + { name: 'agent' } + ]); + }); +}); diff --git a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js index 629fb3284ff114..10e528d19785b4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js @@ -7,7 +7,7 @@ import _ from 'lodash'; import { AbstractLayer } from './layer'; import { VectorLayer } from './vector_layer'; -import { HeatmapStyle } from './styles/heatmap_style'; +import { HeatmapStyle } from './styles/heatmap/heatmap_style'; import { EMPTY_FEATURE_COLLECTION, LAYER_TYPE } from '../../common/constants'; const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__';//unique name to store scaled value for weighting diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js index 8d1d1439a967e3..9c9aa912f3a399 100644 --- a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js +++ b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js @@ -6,7 +6,7 @@ import { ESTermSource } from '../sources/es_term_source'; -import { VectorStyle } from '../styles/vector_style'; +import { VectorStyle } from '../styles/vector/vector_style'; export class InnerJoin { diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js index 45854b7b729f58..6515ab608c2ea1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/layer.js @@ -406,5 +406,9 @@ export class AbstractLayer { mbMap.setLayoutProperty(mbLayerId, 'visibility', this.isVisible() ? 'visible' : 'none'); } + getType() { + return this._descriptor.type; + } + } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js new file mode 100644 index 00000000000000..d9639144dfc529 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js @@ -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 { AbstractESSource } from './es_source'; +import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property'; +import { METRIC_TYPE } from '../../../common/constants'; +import _ from 'lodash'; + +const COUNT_PROP_LABEL = 'count'; +const COUNT_PROP_NAME = 'doc_count'; +const AGG_DELIMITER = '_of_'; + +export class AbstractESAggSource extends AbstractESSource { + + static METRIC_SCHEMA_CONFIG = { + group: 'metrics', + name: 'metric', + title: 'Value', + min: 1, + max: Infinity, + aggFilter: [ + METRIC_TYPE.AVG, + METRIC_TYPE.COUNT, + METRIC_TYPE.MAX, + METRIC_TYPE.MIN, + METRIC_TYPE.SUM, + METRIC_TYPE.UNIQUE_COUNT + ], + defaults: [ + { schema: 'metric', type: METRIC_TYPE.COUNT } + ] + }; + + _formatMetricKey(metric) { + const aggType = metric.type; + const fieldName = metric.field; + return aggType !== METRIC_TYPE.COUNT ? `${aggType}${AGG_DELIMITER}${fieldName}` : COUNT_PROP_NAME; + } + + _formatMetricLabel(metric) { + const aggType = metric.type; + const fieldName = metric.field; + return aggType !== METRIC_TYPE.COUNT ? `${aggType} of ${fieldName}` : COUNT_PROP_LABEL; + } + + _getValidMetrics() { + const metrics = _.get(this._descriptor, 'metrics', []).filter(({ type, field }) => { + if (type === METRIC_TYPE.COUNT) { + return true; + } + + if (field) { + return true; + } + return false; + }); + if (metrics.length === 0) { + metrics.push({ type: METRIC_TYPE.COUNT }); + } + return metrics; + } + + getMetricFields() { + return this._getValidMetrics().map(metric => { + const metricKey = this._formatMetricKey(metric); + const metricLabel = metric.label ? metric.label : this._formatMetricLabel(metric); + const metricCopy = { ...metric }; + delete metricCopy.label; + return { + ...metricCopy, + propertyKey: metricKey, + propertyLabel: metricLabel + }; + }); + } + + async getNumberFields() { + return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { + return { label, name }; + }); + } + + getFieldNames() { + return this.getMetricFields().map(({ propertyKey }) => { + return propertyKey; + }); + } + + createMetricAggConfigs() { + return this.getMetricFields().map(metric => { + const metricAggConfig = { + id: metric.propertyKey, + enabled: true, + type: metric.type, + schema: 'metric', + params: {} + }; + if (metric.type !== METRIC_TYPE.COUNT) { + metricAggConfig.params = { field: metric.field }; + } + return metricAggConfig; + }); + } + + async filterAndFormatPropertiesToHtmlForMetricFields(properties) { + let indexPattern; + try { + indexPattern = await this._getIndexPattern(); + } catch(error) { + console.warn(`Unable to find Index pattern ${this._descriptor.indexPatternId}, values are not formatted`); + return properties; + } + + const metricFields = this.getMetricFields(); + const tooltipProperties = []; + metricFields.forEach((metricField) => { + let value; + for (const key in properties) { + if (properties.hasOwnProperty(key) && metricField.propertyKey === key) { + value = properties[key]; + break; + } + } + + const tooltipProperty = new ESAggMetricTooltipProperty( + metricField.propertyKey, + metricField.propertyLabel, + value, + indexPattern, + metricField + ); + tooltipProperties.push(tooltipProperty); + }); + + return tooltipProperties; + + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js index e06568285dd6b5..c83f12ce992ff0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js @@ -20,8 +20,7 @@ export function convertToGeoJson({ table, renderAs }) { } const metricColumns = table.columns.filter(column => { - return column.aggConfig.type.type === 'metrics' - && column.aggConfig.type.dslName !== 'geo_centroid'; + return column.aggConfig.type.type === 'metrics' && column.aggConfig.type.dslName !== 'geo_centroid'; }); const geocentroidColumn = table.columns.find(column => column.aggConfig.type.dslName === 'geo_centroid'); if (!geocentroidColumn) { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js index 776980e17bb130..a8b52e586da28a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -8,46 +8,29 @@ import React from 'react'; import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; -import { AbstractESSource } from '../es_source'; import { HeatmapLayer } from '../../heatmap_layer'; import { VectorLayer } from '../../vector_layer'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { convertToGeoJson } from './convert_to_geojson'; -import { VectorStyle } from '../../styles/vector_style'; -import { vectorStyles } from '../../styles/vector_style_defaults'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { vectorStyles } from '../../styles/vector/vector_style_defaults'; import { RENDER_AS } from './render_as'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; import { GRID_RESOLUTION } from '../../grid_resolution'; -import { SOURCE_DATA_ID_ORIGIN, ES_GEO_GRID, METRIC_TYPE } from '../../../../common/constants'; +import { SOURCE_DATA_ID_ORIGIN, ES_GEO_GRID } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; +import { AbstractESAggSource } from '../es_agg_source'; const COUNT_PROP_LABEL = 'count'; const COUNT_PROP_NAME = 'doc_count'; const MAX_GEOTILE_LEVEL = 29; const aggSchemas = new Schemas([ - { - group: 'metrics', - name: 'metric', - title: 'Value', - min: 1, - max: Infinity, - aggFilter: [ - METRIC_TYPE.AVG, - METRIC_TYPE.COUNT, - METRIC_TYPE.MAX, - METRIC_TYPE.MIN, - METRIC_TYPE.SUM, - METRIC_TYPE.UNIQUE_COUNT - ], - defaults: [ - { schema: 'metric', type: METRIC_TYPE.COUNT } - ] - }, + AbstractESAggSource.METRIC_SCHEMA_CONFIG, { group: 'buckets', name: 'segment', @@ -58,7 +41,7 @@ const aggSchemas = new Schemas([ } ]); -export class ESGeoGridSource extends AbstractESSource { +export class ESGeoGridSource extends AbstractESAggSource { static type = ES_GEO_GRID; static title = i18n.translate('xpack.maps.source.esGridTitle', { @@ -140,12 +123,6 @@ export class ESGeoGridSource extends AbstractESSource { ]; } - getFieldNames() { - return this.getMetricFields().map(({ propertyKey }) => { - return propertyKey; - }); - } - isGeoGridPrecisionAware() { return true; } @@ -184,12 +161,6 @@ export class ESGeoGridSource extends AbstractESSource { })); } - async getNumberFields() { - return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { - return { label, name }; - }); - } - async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) { const indexPattern = await this._getIndexPattern(); const searchSource = await this._makeSearchSource(searchFilters, 0); @@ -221,29 +192,8 @@ export class ESGeoGridSource extends AbstractESSource { return true; } - _formatMetricKey(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type}_of_${metric.field}` : COUNT_PROP_NAME; - } - - _formatMetricLabel(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type} of ${metric.field}` : COUNT_PROP_LABEL; - } - _makeAggConfigs(precision) { - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); - + const metricAggConfigs = this.createMetricAggConfigs(); return [ ...metricAggConfigs, { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js index 3debfdf1541f71..6cc6ab196f7d87 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js @@ -8,45 +8,26 @@ import React from 'react'; import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; -import { AbstractESSource } from '../es_source'; import { VectorLayer } from '../../vector_layer'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; -import { VectorStyle } from '../../styles/vector_style'; -import { vectorStyles } from '../../styles/vector_style_defaults'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { vectorStyles } from '../../styles/vector/vector_style_defaults'; import { i18n } from '@kbn/i18n'; -import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW, METRIC_TYPE } from '../../../../common/constants'; +import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW } from '../../../../common/constants'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { convertToLines } from './convert_to_lines'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; +import { AbstractESAggSource } from '../es_agg_source'; const COUNT_PROP_LABEL = 'count'; const COUNT_PROP_NAME = 'doc_count'; const MAX_GEOTILE_LEVEL = 29; -const aggSchemas = new Schemas([ - { - group: 'metrics', - name: 'metric', - title: 'Value', - min: 1, - max: Infinity, - aggFilter: [ - METRIC_TYPE.AVG, - METRIC_TYPE.COUNT, - METRIC_TYPE.MAX, - METRIC_TYPE.MIN, - METRIC_TYPE.SUM, - METRIC_TYPE.UNIQUE_COUNT - ], - defaults: [ - { schema: 'metric', type: METRIC_TYPE.COUNT } - ] - } -]); +const aggSchemas = new Schemas([AbstractESAggSource.METRIC_SCHEMA_CONFIG]); -export class ESPewPewSource extends AbstractESSource { +export class ESPewPewSource extends AbstractESAggSource { static type = ES_PEW_PEW; static title = i18n.translate('xpack.maps.source.pewPewTitle', { @@ -103,12 +84,6 @@ export class ESPewPewSource extends AbstractESSource { return true; } - async getNumberFields() { - return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { - return { label, name }; - }); - } - async getSupportedShapeTypes() { return [VECTOR_SHAPE_TYPES.LINE]; } @@ -192,19 +167,7 @@ export class ESPewPewSource extends AbstractESSource { async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) { const indexPattern = await this._getIndexPattern(); - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); + const metricAggConfigs = this.createMetricAggConfigs(); const aggConfigs = new AggConfigs(indexPattern, metricAggConfigs, aggSchemas.all); const searchSource = await this._makeSearchSource(searchFilters, 0); @@ -258,14 +221,6 @@ export class ESPewPewSource extends AbstractESSource { }; } - _formatMetricKey(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type}_of_${metric.field}` : COUNT_PROP_NAME; - } - - _formatMetricLabel(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type} of ${metric.field}` : COUNT_PROP_LABEL; - } - async _getGeoField() { const indexPattern = await this._getIndexPattern(); const geoField = indexPattern.fields.getByName(this._descriptor.destGeoField); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 85c866479a6bac..cc53ee27af4715 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -15,8 +15,6 @@ import { timefilter } from 'ui/timefilter'; import _ from 'lodash'; import { AggConfigs } from 'ui/agg_types'; import { i18n } from '@kbn/i18n'; -import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property'; - import uuid from 'uuid/v4'; import { copyPersistentState } from '../../reducers/util'; import { ES_GEO_FIELD_TYPE, METRIC_TYPE } from '../../../common/constants'; @@ -57,80 +55,6 @@ export class AbstractESSource extends AbstractVectorSource { return clonedDescriptor; } - _getValidMetrics() { - const metrics = _.get(this._descriptor, 'metrics', []).filter(({ type, field }) => { - if (type === METRIC_TYPE.COUNT) { - return true; - } - - if (field) { - return true; - } - return false; - }); - if (metrics.length === 0) { - metrics.push({ type: METRIC_TYPE.COUNT }); - } - return metrics; - } - - _formatMetricKey() { - throw new Error('should implement'); - } - - _formatMetricLabel() { - throw new Error('should implement'); - } - - getMetricFields() { - return this._getValidMetrics().map(metric => { - const metricKey = this._formatMetricKey(metric); - const metricLabel = metric.label ? metric.label : this._formatMetricLabel(metric); - const metricCopy = { ...metric }; - delete metricCopy.label; - return { - ...metricCopy, - propertyKey: metricKey, - propertyLabel: metricLabel - }; - }); - } - - async filterAndFormatPropertiesToHtmlForMetricFields(properties) { - let indexPattern; - try { - indexPattern = await this._getIndexPattern(); - } catch(error) { - console.warn(`Unable to find Index pattern ${this._descriptor.indexPatternId}, values are not formatted`); - return properties; - } - - - const metricFields = this.getMetricFields(); - const tooltipProperties = []; - metricFields.forEach((metricField) => { - let value; - for (const key in properties) { - if (properties.hasOwnProperty(key) && metricField.propertyKey === key) { - value = properties[key]; - break; - } - } - - const tooltipProperty = new ESAggMetricTooltipProperty( - metricField.propertyKey, - metricField.propertyLabel, - value, - indexPattern, - metricField - ); - tooltipProperties.push(tooltipProperty); - }); - - return tooltipProperties; - - } - async _runEsQuery(requestName, searchSource, registerCancelCallback, requestDescription) { const abortController = new AbortController(); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js index 1f5adc00cca6fc..7d1ccf7373cf61 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js @@ -6,34 +6,17 @@ import _ from 'lodash'; -import { AbstractESSource } from './es_source'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; import { i18n } from '@kbn/i18n'; import { ESTooltipProperty } from '../tooltips/es_tooltip_property'; import { ES_SIZE_LIMIT, METRIC_TYPE } from '../../../common/constants'; +import { AbstractESAggSource } from './es_agg_source'; const TERMS_AGG_NAME = 'join'; const aggSchemas = new Schemas([ - { - group: 'metrics', - name: 'metric', - title: 'Value', - min: 1, - max: Infinity, - aggFilter: [ - METRIC_TYPE.AVG, - METRIC_TYPE.COUNT, - METRIC_TYPE.MAX, - METRIC_TYPE.MIN, - METRIC_TYPE.SUM, - METRIC_TYPE.UNIQUE_COUNT - ], - defaults: [ - { schema: 'metric', type: METRIC_TYPE.COUNT } - ] - }, + AbstractESAggSource.METRIC_SCHEMA_CONFIG, { group: 'buckets', name: 'segment', @@ -61,7 +44,7 @@ export function extractPropertiesMap(rawEsData, propertyNames, countPropertyName return propertiesMap; } -export class ESTermSource extends AbstractESSource { +export class ESTermSource extends AbstractESAggSource { static type = 'ES_TERM_SOURCE'; @@ -156,20 +139,7 @@ export class ESTermSource extends AbstractESSource { } _makeAggConfigs() { - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); - + const metricAggConfigs = this.createMetricAggConfigs(); return [ ...metricAggConfigs, { @@ -205,10 +175,4 @@ export class ESTermSource extends AbstractESSource { return null; } } - - getFieldNames() { - return this.getMetricFields().map(({ propertyKey }) => { - return propertyKey; - }); - } } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index b3e8c1b019f28b..8f67d7618049b5 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -7,7 +7,7 @@ import { VectorLayer } from '../vector_layer'; import { TooltipProperty } from '../tooltips/tooltip_property'; -import { VectorStyle } from '../styles/vector_style'; +import { VectorStyle } from '../styles/vector/vector_style'; import { AbstractSource } from './source'; import * as topojson from 'topojson-client'; import _ from 'lodash'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss index 87c7bd1face64b..bd517f81517c23 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss +++ b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss @@ -1,3 +1,3 @@ @import './components/color_gradient'; @import './components/static_dynamic_style_row'; -@import './components/vector/color/color_stops'; +@import './vector/components/color/color_stops'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js index cfe7a0741a194c..5537bb90fa245b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { VectorStyle } from '../vector_style'; +import { VectorStyle } from '../vector/vector_style'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/__snapshots__/heatmap_style_editor.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/__snapshots__/heatmap_style_editor.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_constants.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_constants.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js index 0aa20f29c341b4..a0f86dcf5130be 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js @@ -8,7 +8,7 @@ import React from 'react'; import { EuiFormRow, EuiSuperSelect } from '@elastic/eui'; import { COLOR_GRADIENTS } from '../../color_utils'; -import { ColorGradient } from '../color_gradient'; +import { ColorGradient } from '../../components/color_gradient'; import { DEFAULT_RGB_HEATMAP_COLOR_RAMP, DEFAULT_HEATMAP_COLOR_RAMP_NAME, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js similarity index 88% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js index 74fce11abf0a6a..06709ba0ebf21b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js @@ -7,8 +7,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ColorGradient } from '../../color_gradient'; -import { StyleLegendRow } from '../../style_legend_row'; +import { ColorGradient } from '../../../components/color_gradient'; +import { StyleLegendRow } from '../../../components/style_legend_row'; import { DEFAULT_RGB_HEATMAP_COLOR_RAMP, DEFAULT_HEATMAP_COLOR_RAMP_NAME, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js similarity index 90% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js index 5ccabe610a120f..e537da8a3e2e40 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js @@ -5,12 +5,12 @@ */ import React from 'react'; -import { GRID_RESOLUTION } from '../grid_resolution'; -import { AbstractStyle } from './abstract_style'; -import { HeatmapStyleEditor } from './components/heatmap/heatmap_style_editor'; -import { HeatmapLegend } from './components/heatmap/legend/heatmap_legend'; -import { DEFAULT_HEATMAP_COLOR_RAMP_NAME } from './components/heatmap/heatmap_constants'; -import { getColorRampStops } from './color_utils'; +import { GRID_RESOLUTION } from '../../grid_resolution'; +import { AbstractStyle } from '../abstract_style'; +import { HeatmapStyleEditor } from './components/heatmap_style_editor'; +import { HeatmapLegend } from './components/legend/heatmap_legend'; +import { DEFAULT_HEATMAP_COLOR_RAMP_NAME } from './components/heatmap_constants'; +import { getColorRampStops } from '../color_utils'; import { i18n } from '@kbn/i18n'; import { EuiIcon } from '@elastic/eui'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/__snapshots__/vector_style_symbol_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/__snapshots__/vector_style_symbol_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/_color_stops.scss b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/_color_stops.scss rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_ramp_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_ramp_select.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops_utils.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/dynamic_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/dynamic_color_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/static_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/static_color_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/vector_style_color_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/vector_style_color_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js index c8950ffe71a1ef..a39db57dc4883b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/vector_style_color_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js @@ -6,7 +6,7 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../../static_dynamic_style_row'; +import { StaticDynamicStyleRow } from '../../../components/static_dynamic_style_row'; import { DynamicColorSelection } from './dynamic_color_selection'; import { StaticColorSelection } from './static_color_selection'; import { getVectorStyleLabel } from '../get_vector_style_label'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/field_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/field_select.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/get_vector_style_label.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/get_vector_style_label.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js index a39fabf20bae43..b6cc215ec6ded4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/get_vector_style_label.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; -import { vectorStyles } from '../../vector_style_defaults'; +import { vectorStyles } from '../vector_style_defaults'; export function getVectorStyleLabel(styleName) { switch (styleName) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/__snapshots__/vector_icon.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/__snapshots__/vector_icon.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/circle_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/circle_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/line_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/line_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/polygon_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/polygon_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js index 2f8a603c290ab6..4d091389a360e1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js @@ -9,12 +9,12 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { styleOptionShapes, rangeShape } from '../style_option_shapes'; -import { VectorStyle } from '../../../vector_style'; -import { ColorGradient } from '../../color_gradient'; +import { VectorStyle } from '../../vector_style'; +import { ColorGradient } from '../../../components/color_gradient'; import { CircleIcon } from './circle_icon'; import { getVectorStyleLabel } from '../get_vector_style_label'; import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; -import { StyleLegendRow } from '../../style_legend_row'; +import { StyleLegendRow } from '../../../components/style_legend_row'; function getLineWidthIcons() { const defaultStyle = { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/symbol_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/symbol_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js index a2f8d44604a0a8..d241226e577a80 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/symbol_icon.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { getMakiSymbolSvg, styleSvg, buildSrcUrl } from '../../../symbol_utils'; +import { getMakiSymbolSvg, styleSvg, buildSrcUrl } from '../../symbol_utils'; export class SymbolIcon extends Component { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js index 0f9e76c3e74d9f..5b86b885725521 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js @@ -12,7 +12,7 @@ import { CircleIcon } from './circle_icon'; import { LineIcon } from './line_icon'; import { PolygonIcon } from './polygon_icon'; import { SymbolIcon } from './symbol_icon'; -import { VectorStyle } from '../../../vector_style'; +import { VectorStyle } from '../../vector_style'; import { getColorRampCenterColor } from '../../../color_utils'; export class VectorIcon extends Component { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js index 221b6268c55df7..1a16035fe6d338 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { VectorIcon } from './vector_icon'; -import { VectorStyle } from '../../../vector_style'; +import { VectorStyle } from '../../vector_style'; let isPointsOnly = false; let isLinesOnly = false; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_style_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_style_legend.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/dynamic_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/dynamic_orientation_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/orientation_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/orientation_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js index bfe712d13d3afd..65efddae909333 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/orientation_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js @@ -6,7 +6,7 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../../static_dynamic_style_row'; +import { StaticDynamicStyleRow } from '../../../components/static_dynamic_style_row'; import { DynamicOrientationSelection } from './dynamic_orientation_selection'; import { StaticOrientationSelection } from './static_orientation_selection'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/static_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/static_orientation_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/dynamic_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/dynamic_size_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/size_range_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js similarity index 93% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/size_range_selector.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js index 31b9b4f5ad6498..4279fbbe0fbb35 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/size_range_selector.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ValidatedDualRange } from 'ui/validated_range'; -import { DEFAULT_MIN_SIZE, DEFAULT_MAX_SIZE } from '../../../vector_style_defaults'; +import { DEFAULT_MIN_SIZE, DEFAULT_MAX_SIZE } from '../../vector_style_defaults'; import { i18n } from '@kbn/i18n'; export function SizeRangeSelector({ minSize, maxSize, onChange, ...rest }) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/static_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/static_size_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/vector_style_size_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/vector_style_size_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js index a03835f7a9501a..ffaaace680a43c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/vector_style_size_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js @@ -6,7 +6,7 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../../static_dynamic_style_row'; +import { StaticDynamicStyleRow } from '../../../components/static_dynamic_style_row'; import { DynamicSizeSelection } from './dynamic_size_selection'; import { StaticSizeSelection } from './static_size_selection'; import { getVectorStyleLabel } from '../get_vector_style_label'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/style_option_shapes.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/style_option_shapes.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 83d4ff7c11d66d..f624f4e661a14b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -16,12 +16,12 @@ import { getDefaultDynamicProperties, getDefaultStaticProperties, vectorStyles, -} from '../../vector_style_defaults'; +} from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types'; -import { SYMBOLIZE_AS_ICON } from '../../vector_constants'; +import { SYMBOLIZE_AS_ICON } from '../vector_constants'; import { i18n } from '@kbn/i18n'; -import { SYMBOL_OPTIONS } from '../../symbol_utils'; +import { SYMBOL_OPTIONS } from '../symbol_utils'; import { EuiSpacer, EuiButtonGroup } from '@elastic/eui'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.js index 268b1f39255b94..29be736b432f94 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.js @@ -16,7 +16,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from '../../vector_constants'; +import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from '../vector_constants'; import { SymbolIcon } from './legend/symbol_icon'; const SYMBOLIZE_AS_OPTIONS = [ diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.test.js similarity index 93% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.test.js index 4a2c227d84535f..44b864dea2e991 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_symbol_editor.test.js @@ -7,7 +7,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from '../../vector_constants'; +import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from '../vector_constants'; import { VectorStyleSymbolEditor } from './vector_style_symbol_editor'; const symbolOptions = [ diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js new file mode 100644 index 00000000000000..5c2122dfc4566e --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -0,0 +1,105 @@ +/* + * 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 { DynamicStyleProperty } from './dynamic_style_property'; +import _ from 'lodash'; +import { getComputedFieldName } from '../style_util'; +import { getColorRampStops } from '../../color_utils'; + + +export class DynamicColorProperty extends DynamicStyleProperty { + + + syncCircleColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'circle-color', color); + mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); + } + + syncIconColorWithMb(mbLayerId, mbMap) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'icon-color', color); + } + + syncHaloBorderColorWithMb(mbLayerId, mbMap) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', color); + } + + syncCircleStrokeWithMb(pointLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', color); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); + } + + syncFillColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'fill-color', color); + mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); + } + + syncLineColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'line-color', color); + mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); + } + + _getMbColor() { + const isDynamicConfigComplete = _.has(this._options, 'field.name') && _.has(this._options, 'color'); + if (!isDynamicConfigComplete) { + return null; + } + + if (this._options.useCustomColorRamp && (!this._options.customColorRamp || !this._options.customColorRamp.length)) { + return null; + } + + return this._getMBDataDrivenColor({ + targetName: getComputedFieldName(this._styleName, this._options.field.name), + colorStops: this._getMBColorStops(), + isSteps: this._options.useCustomColorRamp, + }); + } + + _getMBDataDrivenColor({ targetName, colorStops, isSteps }) { + if (isSteps) { + const firstStopValue = colorStops[0]; + const lessThenFirstStopValue = firstStopValue - 1; + return [ + 'step', + ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], + 'rgba(0,0,0,0)', // MB will assign the base value to any features that is below the first stop value + ...colorStops + ]; + } + + return [ + 'interpolate', + ['linear'], + ['coalesce', ['feature-state', targetName], -1], + -1, 'rgba(0,0,0,0)', + ...colorStops + ]; + } + + + _getMBColorStops() { + + if (this._options.useCustomColorRamp) { + return this._options.customColorRamp.reduce((accumulatedStops, nextStop) => { + return [...accumulatedStops, nextStop.stop, nextStop.color]; + }, []); + } + + return getColorRampStops(this._options.color); + } + +} + + + + diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js new file mode 100644 index 00000000000000..2881ee422048bf --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { DynamicStyleProperty } from './dynamic_style_property'; +import { getComputedFieldName } from '../style_util'; +import { vectorStyles } from '../vector_style_defaults'; + + +export class DynamicOrientationProperty extends DynamicStyleProperty { + + syncIconRotationWithMb(symbolLayerId, mbMap) { + if (this._options.field && this._options.field.name) { + const targetName = getComputedFieldName(vectorStyles.ICON_ORIENTATION, this._options.field.name); + // Using property state instead of feature-state because layout properties do not support feature-state + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', ['coalesce', ['get', targetName], 0]); + } else { + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', 0); + } + } + +} + + + + diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js new file mode 100644 index 00000000000000..2758a440f57c5f --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { DynamicStyleProperty } from './dynamic_style_property'; +import { getComputedFieldName } from '../style_util'; +import { HALF_LARGE_MAKI_ICON_SIZE, LARGE_MAKI_ICON_SIZE, SMALL_MAKI_ICON_SIZE } from '../symbol_utils'; +import { vectorStyles } from '../vector_style_defaults'; +import _ from 'lodash'; + +export class DynamicSizeProperty extends DynamicStyleProperty { + + syncHaloWidthWithMb(mbLayerId, mbMap) { + const haloWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', haloWidth); + } + + + syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId) { + if (this._isSizeDynamicConfigComplete(this._options)) { + const iconPixels = this._options.maxSize >= HALF_LARGE_MAKI_ICON_SIZE + ? LARGE_MAKI_ICON_SIZE + : SMALL_MAKI_ICON_SIZE; + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); + + const halfIconPixels = iconPixels / 2; + const targetName = getComputedFieldName(vectorStyles.ICON_SIZE, this._options.field.name); + // Using property state instead of feature-state because layout properties do not support feature-state + mbMap.setLayoutProperty(symbolLayerId, 'icon-size', [ + 'interpolate', + ['linear'], + ['coalesce', ['get', targetName], 0], + 0, this._options.minSize / halfIconPixels, + 1, this._options.maxSize / halfIconPixels + ]); + } else { + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', null); + mbMap.setLayoutProperty(symbolLayerId, 'icon-size', null); + } + } + + syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { + const lineWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', lineWidth); + } + + syncCircleRadiusWithMb(mbLayerId, mbMap) { + const circleRadius = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'circle-radius', circleRadius); + } + + syncLineWidthWithMb(mbLayerId, mbMap) { + const lineWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'line-width', lineWidth); + } + + _getMbSize() { + if (this._isSizeDynamicConfigComplete(this._options)) { + return this._getMbDataDrivenSize({ + targetName: getComputedFieldName(this._styleName, this._options.field.name), + minSize: this._options.minSize, + maxSize: this._options.maxSize, + }); + } + return null; + } + + _getMbDataDrivenSize({ targetName, minSize, maxSize }) { + return [ + 'interpolate', + ['linear'], + ['coalesce', ['feature-state', targetName], 0], + 0, minSize, + 1, maxSize + ]; + } + + _isSizeDynamicConfigComplete() { + return this._options.field && this._options.field.name && _.has(this._options, 'minSize') && _.has(this._options, 'maxSize'); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js new file mode 100644 index 00000000000000..8200ede3e3523b --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { AbstractStyleProperty } from './style_property'; + +export class DynamicStyleProperty extends AbstractStyleProperty { + static type = 'DYNAMIC'; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js new file mode 100644 index 00000000000000..d4c44fca1bd082 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js @@ -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 { StaticStyleProperty } from './static_style_property'; + + +export class StaticColorProperty extends StaticStyleProperty { + + syncCircleColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'circle-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); + } + + syncFillColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'fill-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); + } + + syncIconColorWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-color', this._options.color); + } + + syncHaloBorderColorWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', this._options.color); + } + + syncLineColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'line-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); + } + + syncCircleStrokeWithMb(pointLayerId, mbMap, alpha) { + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', this._options.color); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js new file mode 100644 index 00000000000000..48a7bbd3f79df2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js @@ -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 { StaticStyleProperty } from './static_style_property'; + + +export class StaticOrientationProperty extends StaticStyleProperty { + + constructor(options, styleName) { + if (typeof options.orientation !== 'number') { + super({ orientation: 0 }, styleName); + } else { + super(options, styleName); + } + } + + syncIconRotationWithMb(symbolLayerId, mbMap) { + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', this._options.orientation); + } + + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js new file mode 100644 index 00000000000000..37162d8cb0a3c1 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { StaticStyleProperty } from './static_style_property'; +import { HALF_LARGE_MAKI_ICON_SIZE, LARGE_MAKI_ICON_SIZE, SMALL_MAKI_ICON_SIZE } from '../symbol_utils'; + + +export class StaticSizeProperty extends StaticStyleProperty { + + constructor(options, styleName) { + if (typeof options.size !== 'number') { + super({ size: 1 }, styleName); + } else { + super(options, styleName); + } + } + + syncHaloWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', this._options.size); + } + + syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId) { + const iconPixels = this._size >= HALF_LARGE_MAKI_ICON_SIZE ? LARGE_MAKI_ICON_SIZE : SMALL_MAKI_ICON_SIZE; + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); + const halfIconPixels = iconPixels / 2; + mbMap.setLayoutProperty(symbolLayerId, 'icon-size', this._options.size / halfIconPixels); + } + + syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', this._options.size); + } + + syncCircleRadiusWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'circle-radius', this._options.size); + } + + syncLineWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'line-width', this._options.size); + } + + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js new file mode 100644 index 00000000000000..448efc06899e56 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { AbstractStyleProperty } from './style_property'; + +export class StaticStyleProperty extends AbstractStyleProperty { + static type = 'STATIC'; + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js new file mode 100644 index 00000000000000..7e9e27f83722dc --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export class AbstractStyleProperty { + + constructor(options, styleName) { + this._options = options; + this._styleName = styleName; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js new file mode 100644 index 00000000000000..69caaca0801389 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js @@ -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. + */ + + +export function getComputedFieldName(styleName, fieldName) { + return `${getComputedFieldNamePrefix(fieldName)}__${styleName}`; +} + +export function getComputedFieldNamePrefix(fieldName) { + return `__kbn__dynamic__${fieldName}`; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js index 22e1a6aea6a729..967d0e0bbd0969 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js @@ -6,7 +6,7 @@ import maki from '@elastic/maki'; import xml2js from 'xml2js'; -import { parseXmlString } from '../../../common/parse_xml_string'; +import { parseXmlString } from '../../../../common/parse_xml_string'; export const LARGE_MAKI_ICON_SIZE = 15; const LARGE_MAKI_ICON_SIZE_AS_STRING = LARGE_MAKI_ICON_SIZE.toString(); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_constants.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_constants.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_constants.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js similarity index 58% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index f26f4df0b17537..70ebba7e8d177c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -7,34 +7,36 @@ import _ from 'lodash'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { getColorRampStops } from './color_utils'; -import { VectorStyleEditor } from './components/vector/vector_style_editor'; +import { VectorStyleEditor } from './components/vector_style_editor'; import { getDefaultProperties, vectorStyles } from './vector_style_defaults'; -import { AbstractStyle } from './abstract_style'; -import { SOURCE_DATA_ID_ORIGIN, GEO_JSON_TYPE } from '../../../common/constants'; -import { VectorIcon } from './components/vector/legend/vector_icon'; -import { VectorStyleLegend } from './components/vector/legend/vector_style_legend'; -import { VECTOR_SHAPE_TYPES } from '../sources/vector_feature_types'; +import { AbstractStyle } from '../abstract_style'; +import { SOURCE_DATA_ID_ORIGIN, GEO_JSON_TYPE } from '../../../../common/constants'; +import { VectorIcon } from './components/legend/vector_icon'; +import { VectorStyleLegend } from './components/legend/vector_style_legend'; +import { VECTOR_SHAPE_TYPES } from '../../sources/vector_feature_types'; import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from './vector_constants'; -import { - getMakiSymbolAnchor, - LARGE_MAKI_ICON_SIZE, - SMALL_MAKI_ICON_SIZE, - HALF_LARGE_MAKI_ICON_SIZE -} from './symbol_utils'; +import { getMakiSymbolAnchor } from './symbol_utils'; +import { getComputedFieldName, getComputedFieldNamePrefix } from './style_util'; +import { StaticStyleProperty } from './properties/static_style_property'; +import { DynamicStyleProperty } from './properties/dynamic_style_property'; +import { DynamicSizeProperty } from './properties/dynamic_size_property'; +import { StaticSizeProperty } from './properties/static_size_property'; +import { StaticColorProperty } from './properties/static_color_property'; +import { DynamicColorProperty } from './properties/dynamic_color_property'; +import { StaticOrientationProperty } from './properties/static_orientation_property'; +import { DynamicOrientationProperty } from './properties/dynamic_orientation_property'; + +const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT]; +const LINES = [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING]; +const POLYGONS = [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON]; export class VectorStyle extends AbstractStyle { static type = 'VECTOR'; - static STYLE_TYPE = { 'DYNAMIC': 'DYNAMIC', 'STATIC': 'STATIC' }; + static STYLE_TYPE = { 'DYNAMIC': DynamicStyleProperty.type, 'STATIC': StaticStyleProperty.type }; - static getComputedFieldName(styleName, fieldName) { - return `${VectorStyle.getComputedFieldNamePrefix(fieldName)}__${styleName}`; - } - - static getComputedFieldNamePrefix(fieldName) { - return `__kbn__dynamic__${fieldName}`; - } + static getComputedFieldName = getComputedFieldName; + static getComputedFieldNamePrefix = getComputedFieldNamePrefix; constructor(descriptor = {}, source) { super(); @@ -43,6 +45,13 @@ export class VectorStyle extends AbstractStyle { ...descriptor, ...VectorStyle.createDescriptor(descriptor.properties), }; + + this._lineColorStyleProperty = this._makeColorProperty(this._descriptor.properties[vectorStyles.LINE_COLOR], vectorStyles.LINE_COLOR); + this._fillColorStyleProperty = this._makeColorProperty(this._descriptor.properties[vectorStyles.FILL_COLOR], vectorStyles.FILL_COLOR); + this._lineWidthStyleProperty = this._makeSizeProperty(this._descriptor.properties[vectorStyles.LINE_WIDTH], vectorStyles.LINE_WIDTH); + this._iconSizeStyleProperty = this._makeSizeProperty(this._descriptor.properties[vectorStyles.ICON_SIZE], vectorStyles.ICON_SIZE); + // eslint-disable-next-line max-len + this._iconOrientationProperty = this._makeOrientationProperty(this._descriptor.properties[vectorStyles.ICON_ORIENTATION], vectorStyles.ICON_ORIENTATION); } static createDescriptor(properties = {}) { @@ -168,13 +177,13 @@ export class VectorStyle extends AbstractStyle { let hasPolygons = false; for (let i = 0; i < features.length; i++) { const feature = features[i]; - if (!hasPoints && [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT].includes(feature.geometry.type)) { + if (!hasPoints && POINTS.includes(feature.geometry.type)) { hasPoints = true; } - if (!hasLines && [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING].includes(feature.geometry.type)) { + if (!hasLines && LINES.includes(feature.geometry.type)) { hasLines = true; } - if (!hasPolygons && [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON].includes(feature.geometry.type)) { + if (!hasPolygons && POLYGONS.includes(feature.geometry.type)) { hasPolygons = true; } @@ -430,210 +439,72 @@ export class VectorStyle extends AbstractStyle { return hasGeoJsonProperties; } - _getMBDataDrivenColor({ targetName, colorStops, isSteps }) { - if (isSteps) { - const firstStopValue = colorStops[0]; - const lessThenFirstStopValue = firstStopValue - 1; - return [ - 'step', - ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], - 'rgba(0,0,0,0)', // MB will assign the base value to any features that is below the first stop value - ...colorStops - ]; - } - - return [ - 'interpolate', - ['linear'], - ['coalesce', ['feature-state', targetName], -1], - -1, 'rgba(0,0,0,0)', - ...colorStops - ]; - } - - _getMbDataDrivenSize({ targetName, minSize, maxSize }) { - return [ - 'interpolate', - ['linear'], - ['coalesce', ['feature-state', targetName], 0], - 0, minSize, - 1, maxSize - ]; + arePointsSymbolizedAsCircles() { + return this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_CIRCLE; } - _getMBColor(styleName, styleDescriptor) { - const isStatic = styleDescriptor.type === VectorStyle.STYLE_TYPE.STATIC; - if (isStatic) { - return _.get(styleDescriptor, 'options.color', null); - } - - const isDynamicConfigComplete = _.has(styleDescriptor, 'options.field.name') - && _.has(styleDescriptor, 'options.color'); - if (!isDynamicConfigComplete) { - return null; - } - - if (styleDescriptor.options.useCustomColorRamp && - (!styleDescriptor.options.customColorRamp || - !styleDescriptor.options.customColorRamp.length)) { - return null; - } - - return this._getMBDataDrivenColor({ - targetName: VectorStyle.getComputedFieldName(styleName, styleDescriptor.options.field.name), - colorStops: this._getMBColorStops(styleDescriptor), - isSteps: styleDescriptor.options.useCustomColorRamp, - }); + setMBPaintProperties({ alpha, mbMap, fillLayerId, lineLayerId }) { + this._fillColorStyleProperty.syncFillColorWithMb(fillLayerId, mbMap, alpha); + this._lineColorStyleProperty.syncLineColorWithMb(lineLayerId, mbMap, alpha); + this._lineWidthStyleProperty.syncLineWidthWithMb(lineLayerId, mbMap); } - _getMBColorStops(styleDescriptor) { - if (styleDescriptor.options.useCustomColorRamp) { - return styleDescriptor.options.customColorRamp.reduce((accumulatedStops, nextStop) => { - return [...accumulatedStops, nextStop.stop, nextStop.color]; - }, []); - } - - return getColorRampStops(styleDescriptor.options.color); + setMBPaintPropertiesForPoints({ alpha, mbMap, pointLayerId }) { + this._fillColorStyleProperty.syncCircleColorWithMb(pointLayerId, mbMap, alpha); + this._lineColorStyleProperty.syncCircleStrokeWithMb(pointLayerId, mbMap, alpha); + this._lineWidthStyleProperty.syncCircleStrokeWidthWithMb(pointLayerId, mbMap); + this._iconSizeStyleProperty.syncCircleRadiusWithMb(pointLayerId, mbMap); } - _isSizeDynamicConfigComplete(styleDescriptor) { - return _.has(styleDescriptor, 'options.field.name') - && _.has(styleDescriptor, 'options.minSize') - && _.has(styleDescriptor, 'options.maxSize'); - } + setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) { - _getMbSize(styleName, styleDescriptor) { - if (styleDescriptor.type === VectorStyle.STYLE_TYPE.STATIC) { - return styleDescriptor.options.size; - } + const symbolId = this._descriptor.properties.symbol.options.symbolId; + mbMap.setLayoutProperty(symbolLayerId, 'icon-ignore-placement', true); + mbMap.setLayoutProperty(symbolLayerId, 'icon-anchor', getMakiSymbolAnchor(symbolId)); + mbMap.setPaintProperty(symbolLayerId, 'icon-opacity', alpha); - if (this._isSizeDynamicConfigComplete(styleDescriptor)) { - return this._getMbDataDrivenSize({ - targetName: VectorStyle.getComputedFieldName(styleName, styleDescriptor.options.field.name), - minSize: styleDescriptor.options.minSize, - maxSize: styleDescriptor.options.maxSize, - }); - } + // icon-color is only supported on SDF icons. + this._fillColorStyleProperty.syncIconColorWithMb(symbolLayerId, mbMap); + this._lineColorStyleProperty.syncHaloBorderColorWithMb(symbolLayerId, mbMap); + this._lineWidthStyleProperty.syncHaloWidthWithMb(symbolLayerId, mbMap); + this._iconSizeStyleProperty.syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId); + this._iconOrientationProperty.syncIconRotationWithMb(symbolLayerId, mbMap); - return null; } - setMBPaintProperties({ alpha, mbMap, fillLayerId, lineLayerId }) { - if (this._descriptor.properties.fillColor) { - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - mbMap.setPaintProperty(fillLayerId, 'fill-color', color); - mbMap.setPaintProperty(fillLayerId, 'fill-opacity', alpha); - } else { - mbMap.setPaintProperty(fillLayerId, 'fill-color', null); - mbMap.setPaintProperty(fillLayerId, 'fill-opacity', 0); - } - - if (this._descriptor.properties.lineColor) { - const color = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - mbMap.setPaintProperty(lineLayerId, 'line-color', color); - mbMap.setPaintProperty(lineLayerId, 'line-opacity', alpha); - - } else { - mbMap.setPaintProperty(lineLayerId, 'line-color', null); - mbMap.setPaintProperty(lineLayerId, 'line-opacity', 0); - } - - if (this._descriptor.properties.lineWidth) { - const lineWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - mbMap.setPaintProperty(lineLayerId, 'line-width', lineWidth); + _makeSizeProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticSizeProperty({ size: 0 }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticSizeProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + return new DynamicSizeProperty(descriptor.options, styleName); } else { - mbMap.setPaintProperty(lineLayerId, 'line-width', 0); + throw new Error(`${descriptor} not implemented`); } } - setMBPaintPropertiesForPoints({ alpha, mbMap, pointLayerId }) { - if (this._descriptor.properties.fillColor) { - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - mbMap.setPaintProperty(pointLayerId, 'circle-color', color); - mbMap.setPaintProperty(pointLayerId, 'circle-opacity', alpha); - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-color', null); - mbMap.setPaintProperty(pointLayerId, 'circle-opacity', 0); - } - if (this._descriptor.properties.lineColor) { - const color = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', color); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); - - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', null); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', 0); - } - if (this._descriptor.properties.lineWidth) { - const lineWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-width', lineWidth); - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-width', 0); - } - if (this._descriptor.properties.iconSize) { - const iconSize = this._getMbSize(vectorStyles.ICON_SIZE, this._descriptor.properties.iconSize); - mbMap.setPaintProperty(pointLayerId, 'circle-radius', iconSize); + _makeColorProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticColorProperty({ color: null }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticColorProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + return new DynamicColorProperty(descriptor.options, styleName); } else { - mbMap.setPaintProperty(pointLayerId, 'circle-radius', 0); + throw new Error(`${descriptor} not implemented`); } } - async setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) { - mbMap.setLayoutProperty(symbolLayerId, 'icon-ignore-placement', true); - - const symbolId = this._descriptor.properties.symbol.options.symbolId; - mbMap.setLayoutProperty(symbolLayerId, 'icon-anchor', getMakiSymbolAnchor(symbolId)); - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - const haloColor = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - const haloWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - // icon-color is only supported on SDF icons. - mbMap.setPaintProperty(symbolLayerId, 'icon-color', color); - mbMap.setPaintProperty(symbolLayerId, 'icon-halo-color', haloColor); - mbMap.setPaintProperty(symbolLayerId, 'icon-halo-width', haloWidth); - mbMap.setPaintProperty(symbolLayerId, 'icon-opacity', alpha); - - // circle sizing is by radius - // to make icons be similiar in size to circles then have to deal with icon in half width measurements - const iconSize = this._descriptor.properties.iconSize; - if (iconSize.type === VectorStyle.STYLE_TYPE.STATIC) { - const iconPixels = iconSize.options.size >= HALF_LARGE_MAKI_ICON_SIZE - ? LARGE_MAKI_ICON_SIZE - : SMALL_MAKI_ICON_SIZE; - mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); - - const halfIconPixels = iconPixels / 2; - mbMap.setLayoutProperty(symbolLayerId, 'icon-size', iconSize.options.size / halfIconPixels); - } else if (this._isSizeDynamicConfigComplete(iconSize)) { - const iconPixels = iconSize.options.maxSize >= HALF_LARGE_MAKI_ICON_SIZE - ? LARGE_MAKI_ICON_SIZE - : SMALL_MAKI_ICON_SIZE; - mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); - - const halfIconPixels = iconPixels / 2; - const targetName = VectorStyle.getComputedFieldName(vectorStyles.ICON_SIZE, iconSize.options.field.name); - // Using property state instead of feature-state because layout properties do not support feature-state - mbMap.setLayoutProperty(symbolLayerId, 'icon-size', [ - 'interpolate', - ['linear'], - ['coalesce', ['get', targetName], 0], - 0, iconSize.options.minSize / halfIconPixels, - 1, iconSize.options.maxSize / halfIconPixels - ]); - } - - const iconOrientation = this._descriptor.properties.iconOrientation; - if (iconOrientation.type === VectorStyle.STYLE_TYPE.STATIC) { - mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', iconOrientation.options.orientation); - } else if (_.has(iconOrientation, 'options.field.name')) { - const targetName = VectorStyle.getComputedFieldName(vectorStyles.ICON_ORIENTATION, iconOrientation.options.field.name); - // Using property state instead of feature-state because layout properties do not support feature-state - mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', [ - 'coalesce', ['get', targetName], 0 - ]); + _makeOrientationProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticOrientationProperty({ orientation: 0 }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticOrientationProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + return new DynamicOrientationProperty(descriptor.options, styleName); + } else { + throw new Error(`${descriptor} not implemented`); } } - - arePointsSymbolizedAsCircles() { - return this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_CIRCLE; - } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js index 7c993564018aa0..c0d76fadc01a5d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js @@ -5,8 +5,8 @@ */ import { VectorStyle } from './vector_style'; -import { DataRequest } from '../util/data_request'; -import { VECTOR_SHAPE_TYPES } from '../sources/vector_feature_types'; +import { DataRequest } from '../../util/data_request'; +import { VECTOR_SHAPE_TYPES } from '../../sources/vector_feature_types'; describe('getDescriptorWithMissingStylePropsRemoved', () => { const fieldName = 'doIStillExist'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js index dfd0c1ca1b86b4..ea4228430d13d0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js @@ -10,7 +10,7 @@ import { COLOR_GRADIENTS, DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS -} from './color_utils'; +} from '../color_utils'; const DEFAULT_ICON = 'airfield'; diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.js b/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.js index dae56d5526b0ac..446978a151d648 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.js @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildPhraseFilter } from '@kbn/es-query'; import { TooltipProperty } from './tooltip_property'; import _ from 'lodash'; - +import { esFilters } from '../../../../../../../src/plugins/data/public'; export class ESTooltipProperty extends TooltipProperty { constructor(propertyKey, propertyName, rawValue, indexPattern) { @@ -36,7 +35,7 @@ export class ESTooltipProperty extends TooltipProperty { async getESFilters() { return [ - buildPhraseFilter( + esFilters.buildPhraseFilter( this._indexPattern.fields.getByName(this._propertyName), this._rawValue, this._indexPattern) diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js index a8c63418635173..bf6f2bcf6f08bd 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js @@ -7,6 +7,9 @@ // Refresh only query is query where timestamps are different but query is the same. // Triggered by clicking "Refresh" button in QueryBar export function isRefreshOnlyQuery(prevQuery, newQuery) { + if (!prevQuery || !newQuery) { + return false; + } return prevQuery.queryLastTriggeredAt !== newQuery.queryLastTriggeredAt && prevQuery.language === newQuery.language && prevQuery.query === newQuery.query; diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index 7372b549f64236..b2f45cb6206c68 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -7,7 +7,7 @@ import turf from 'turf'; import React from 'react'; import { AbstractLayer } from './layer'; -import { VectorStyle } from './styles/vector_style'; +import { VectorStyle } from './styles/vector/vector_style'; import { InnerJoin } from './joins/inner_join'; import { GEO_JSON_TYPE, diff --git a/x-pack/legacy/plugins/maps/public/reducers/non_serializable_instances.js b/x-pack/legacy/plugins/maps/public/reducers/non_serializable_instances.js index 5fb09b8c87006a..a76330f4d17646 100644 --- a/x-pack/legacy/plugins/maps/public/reducers/non_serializable_instances.js +++ b/x-pack/legacy/plugins/maps/public/reducers/non_serializable_instances.js @@ -10,6 +10,7 @@ import { MapAdapter } from '../inspector/adapters/map_adapter'; const REGISTER_CANCEL_CALLBACK = 'REGISTER_CANCEL_CALLBACK'; const UNREGISTER_CANCEL_CALLBACK = 'UNREGISTER_CANCEL_CALLBACK'; +const SET_EVENT_HANDLERS = 'SET_EVENT_HANDLERS'; function createInspectorAdapters() { const inspectorAdapters = { @@ -27,6 +28,7 @@ export function nonSerializableInstances(state, action = {}) { return { inspectorAdapters: createInspectorAdapters(), cancelRequestCallbacks: new Map(), // key is request token, value is cancel callback + eventHandlers: {}, }; } @@ -41,6 +43,12 @@ export function nonSerializableInstances(state, action = {}) { return { ...state, }; + case SET_EVENT_HANDLERS: { + return { + ...state, + eventHandlers: action.eventHandlers + }; + } default: return state; @@ -57,6 +65,10 @@ export const getCancelRequestCallbacks = ({ nonSerializableInstances }) => { return nonSerializableInstances.cancelRequestCallbacks; }; +export const getEventHandlers = ({ nonSerializableInstances }) => { + return nonSerializableInstances.eventHandlers; +}; + // Actions export const registerCancelCallback = (requestToken, callback) => { return { @@ -86,3 +98,10 @@ export const cancelRequest = (requestToken) => { } }; }; + +export const setEventHandlers = (eventHandlers = {}) => { + return { + type: SET_EVENT_HANDLERS, + eventHandlers, + }; +}; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js index efcac8a9175ca7..4f561c9391d4d8 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js @@ -11,8 +11,8 @@ import { VectorTileLayer } from '../layers/vector_tile_layer'; import { VectorLayer } from '../layers/vector_layer'; import { HeatmapLayer } from '../layers/heatmap_layer'; import { ALL_SOURCES } from '../layers/sources/all_sources'; -import { VectorStyle } from '../layers/styles/vector_style'; -import { HeatmapStyle } from '../layers/styles/heatmap_style'; +import { VectorStyle } from '../layers/styles/vector/vector_style'; +import { HeatmapStyle } from '../layers/styles/heatmap/heatmap_style'; import { timefilter } from 'ui/timefilter'; import { getInspectorAdapters } from '../reducers/non_serializable_instances'; import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; diff --git a/x-pack/legacy/plugins/ml/common/types/modules.ts b/x-pack/legacy/plugins/ml/common/types/modules.ts index 18879304d7c7f2..9eb77d2140323a 100644 --- a/x-pack/legacy/plugins/ml/common/types/modules.ts +++ b/x-pack/legacy/plugins/ml/common/types/modules.ts @@ -80,3 +80,5 @@ export interface DataRecognizerConfigResponse { dashboard: KibanaObjectResponse; }; } + +export type JobOverride = Partial; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts index f99f9661f12ef2..385d50215cd21d 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts @@ -29,6 +29,15 @@ interface RegressionAnalysis { }; } +export const SEARCH_SIZE = 1000; + +export enum INDEX_STATUS { + UNUSED, + LOADING, + LOADED, + ERROR, +} + export interface Eval { meanSquaredError: number | ''; rSquared: number | ''; @@ -91,6 +100,16 @@ export const getPredictionFieldName = (analysis: AnalysisConfig) => { return predictionFieldName; }; +export const getPredictedFieldName = (resultsField: string, analysis: AnalysisConfig) => { + // default is 'ml' + const predictionFieldName = getPredictionFieldName(analysis); + const defaultPredictionField = `${getDependentVar(analysis)}_prediction`; + const predictedField = `${resultsField}.${ + predictionFieldName ? predictionFieldName : defaultPredictionField + }`; + return predictedField; +}; + export const isOutlierAnalysis = (arg: any): arg is OutlierAnalysis => { const keys = Object.keys(arg); return keys.length === 1 && keys[0] === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts index 4ae3e8513e5b4a..5621d77f664697 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts @@ -5,6 +5,7 @@ */ import { getNestedProperty } from '../../util/object_utils'; +import { DataFrameAnalyticsConfig, getPredictedFieldName, getDependentVar } from './analytics'; export type EsId = string; export type EsDocSource = Record; @@ -16,6 +17,7 @@ export interface EsDoc extends Record { } export const MAX_COLUMNS = 20; +export const DEFAULT_REGRESSION_COLUMNS = 8; const ML__ID_COPY = 'ml__id_copy'; @@ -68,6 +70,104 @@ export const sortColumns = (obj: EsDocSource, resultsField: string) => (a: strin return a.localeCompare(b); }; +export const sortRegressionResultsFields = ( + a: string, + b: string, + jobConfig: DataFrameAnalyticsConfig +) => { + const dependentVariable = getDependentVar(jobConfig.analysis); + const resultsField = jobConfig.dest.results_field; + const predictedField = getPredictedFieldName(resultsField, jobConfig.analysis); + if (a === `${resultsField}.is_training`) { + return -1; + } + if (b === `${resultsField}.is_training`) { + return 1; + } + if (a === predictedField) { + return -1; + } + if (b === predictedField) { + return 1; + } + if (a === dependentVariable) { + return -1; + } + if (b === dependentVariable) { + return 1; + } + return a.localeCompare(b); +}; + +// Used to sort columns: +// Anchor on the left ml.is_training, , +export const sortRegressionResultsColumns = ( + obj: EsDocSource, + jobConfig: DataFrameAnalyticsConfig +) => (a: string, b: string) => { + const dependentVariable = getDependentVar(jobConfig.analysis); + const resultsField = jobConfig.dest.results_field; + const predictedField = getPredictedFieldName(resultsField, jobConfig.analysis); + + const typeofA = typeof obj[a]; + const typeofB = typeof obj[b]; + + if (a === `${resultsField}.is_training`) { + return -1; + } + + if (b === `${resultsField}.is_training`) { + return 1; + } + + if (a === predictedField) { + return -1; + } + + if (b === predictedField) { + return 1; + } + + if (a === dependentVariable) { + return -1; + } + + if (b === dependentVariable) { + return 1; + } + + if (typeofA !== 'string' && typeofB === 'string') { + return 1; + } + if (typeofA === 'string' && typeofB !== 'string') { + return -1; + } + if (typeofA === 'string' && typeofB === 'string') { + return a.localeCompare(b); + } + + const tokensA = a.split('.'); + const prefixA = tokensA[0]; + const tokensB = b.split('.'); + const prefixB = tokensB[0]; + + if (prefixA === resultsField && tokensA.length > 1 && prefixB !== resultsField) { + tokensA.shift(); + tokensA.shift(); + if (tokensA.join('.') === b) return 1; + return tokensA.join('.').localeCompare(b); + } + + if (prefixB === resultsField && tokensB.length > 1 && prefixA !== resultsField) { + tokensB.shift(); + tokensB.shift(); + if (tokensB.join('.') === a) return -1; + return a.localeCompare(tokensB.join('.')); + } + + return a.localeCompare(b); +}; + export function getFlattenedFields(obj: EsDocSource, resultsField: string): EsFieldName[] { const flatDocFields: EsFieldName[] = []; const newDocFields = Object.keys(obj); @@ -84,6 +184,39 @@ export function getFlattenedFields(obj: EsDocSource, resultsField: string): EsFi return flatDocFields.filter(f => f !== ML__ID_COPY); } +export const getDefaultRegressionFields = ( + docs: EsDoc[], + jobConfig: DataFrameAnalyticsConfig +): EsFieldName[] => { + const resultsField = jobConfig.dest.results_field; + if (docs.length === 0) { + return []; + } + + const newDocFields = getFlattenedFields(docs[0]._source, resultsField); + return newDocFields + .filter(k => { + if (k === `${resultsField}.is_training`) { + return true; + } + // predicted value of dependent variable + if (k === getPredictedFieldName(resultsField, jobConfig.analysis)) { + return true; + } + // actual value of dependent variable + if (k === getDependentVar(jobConfig.analysis)) { + return true; + } + if (k.split('.')[0] === resultsField) { + return false; + } + + return docs.some(row => row._source[k] !== null); + }) + .sort((a, b) => sortRegressionResultsFields(a, b, jobConfig)) + .slice(0, DEFAULT_REGRESSION_COLUMNS); +}; + export const getDefaultSelectableFields = (docs: EsDoc[], resultsField: string): EsFieldName[] => { if (docs.length === 0) { return []; @@ -99,14 +232,7 @@ export const getDefaultSelectableFields = (docs: EsDoc[], resultsField: string): return false; } - let value = false; - docs.forEach(row => { - const source = row._source; - if (source[k] !== null) { - value = true; - } - }); - return value; + return docs.some(row => row._source[k] !== null); }) .slice(0, MAX_COLUMNS); }; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts index 774db35f2a52b3..112f828f9897eb 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts @@ -21,12 +21,18 @@ export { getValuesFromResponse, loadEvalData, Eval, + getPredictedFieldName, + INDEX_STATUS, + SEARCH_SIZE, } from './analytics'; export { getDefaultSelectableFields, + getDefaultRegressionFields, getFlattenedFields, sortColumns, + sortRegressionResultsColumns, + sortRegressionResultsFields, toggleSelectedField, EsId, EsDoc, diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx index ce39cecb5b5e69..92f438459128e7 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx @@ -6,6 +6,7 @@ import { shallow } from 'enzyme'; import React from 'react'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; jest.mock('../../../../../contexts/ui/use_ui_chrome_context'); jest.mock('ui/new_platform'); @@ -20,7 +21,9 @@ jest.mock('react', () => { describe('Data Frame Analytics: ', () => { test('Minimal initialization', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); // Without the jobConfig being loaded, the component will just return empty. expect(wrapper.text()).toMatch(''); // TODO Once React 16.9 is available we can write tests covering asynchronous hooks. diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx index 95ec3346675474..11bb62dec1624e 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx @@ -50,10 +50,13 @@ import { EsFieldName, EsDoc, MAX_COLUMNS, + INDEX_STATUS, } from '../../../../common'; import { getOutlierScoreFieldName } from './common'; -import { INDEX_STATUS, useExploreData } from './use_explore_data'; +import { useExploreData } from './use_explore_data'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; +import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; const customColorScaleFactory = (n: number) => (t: number) => { if (t < 1 / n) { @@ -78,7 +81,7 @@ const ExplorationTitle: React.SFC<{ jobId: string }> = ({ jobId }) => ( {i18n.translate('xpack.ml.dataframe.analytics.exploration.jobIdTitle', { - defaultMessage: 'Job ID {jobId}', + defaultMessage: 'Outlier detection job ID {jobId}', values: { jobId }, })} @@ -87,9 +90,10 @@ const ExplorationTitle: React.SFC<{ jobId: string }> = ({ jobId }) => ( interface Props { jobId: string; + jobStatus: DATA_FRAME_TASK_STATE; } -export const Exploration: FC = React.memo(({ jobId }) => { +export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { const [jobConfig, setJobConfig] = useState(undefined); const [pageIndex, setPageIndex] = useState(0); @@ -378,7 +382,14 @@ export const Exploration: FC = React.memo(({ jobId }) => { if (status === INDEX_STATUS.LOADED && tableItems.length === 0) { return ( - + + + + + + {getTaskStateBadge(jobStatus)} + + = React.memo(({ jobId }) => { - + + + + + + {getTaskStateBadge(jobStatus)} + + diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts index ee86156c50d4dc..2a07bc1251a311 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts @@ -18,19 +18,12 @@ import { getFlattenedFields, DataFrameAnalyticsConfig, EsFieldName, + INDEX_STATUS, + SEARCH_SIZE, } from '../../../../common'; import { getOutlierScoreFieldName } from './common'; -const SEARCH_SIZE = 1000; - -export enum INDEX_STATUS { - UNUSED, - LOADING, - LOADED, - ERROR, -} - type TableItem = Record; interface LoadExploreDataArg { diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx index c7ea3421ac5dee..20ab6678da8204 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx @@ -8,12 +8,20 @@ import React, { FC, Fragment, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiStat, EuiTitle } from '@elastic/eui'; import { ErrorCallout } from './error_callout'; -import { getValuesFromResponse, loadEvalData, Eval } from '../../../../common'; +import { + getValuesFromResponse, + getDependentVar, + getPredictionFieldName, + loadEvalData, + Eval, + DataFrameAnalyticsConfig, +} from '../../../../common'; +import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; interface Props { - jobId: string; - index: string; - dependentVariable: string; + jobConfig: DataFrameAnalyticsConfig; + jobStatus: DATA_FRAME_TASK_STATE; } const meanSquaredErrorText = i18n.translate( @@ -30,23 +38,28 @@ const rSquaredText = i18n.translate( ); const defaultEval: Eval = { meanSquaredError: '', rSquared: '', error: null }; -export const EvaluatePanel: FC = ({ jobId, index, dependentVariable }) => { +export const EvaluatePanel: FC = ({ jobConfig, jobStatus }) => { const [trainingEval, setTrainingEval] = useState(defaultEval); const [generalizationEval, setGeneralizationEval] = useState(defaultEval); const [isLoadingTraining, setIsLoadingTraining] = useState(false); const [isLoadingGeneralization, setIsLoadingGeneralization] = useState(false); + const index = jobConfig.dest.index; + const dependentVariable = getDependentVar(jobConfig.analysis); + const predictionFieldName = getPredictionFieldName(jobConfig.analysis); + // default is 'ml' + const resultsField = jobConfig.dest.results_field; + const loadData = async () => { setIsLoadingGeneralization(true); setIsLoadingTraining(true); - // TODO: resultsField and predictionFieldName will need to be properly passed to this function - // once the results view is in use. + const genErrorEval = await loadEvalData({ isTraining: false, index, dependentVariable, - resultsField: 'ml', - predictionFieldName: undefined, + resultsField, + predictionFieldName, }); if (genErrorEval.success === true && genErrorEval.eval) { @@ -65,14 +78,13 @@ export const EvaluatePanel: FC = ({ jobId, index, dependentVariable }) => error: genErrorEval.error, }); } - // TODO: resultsField and predictionFieldName will need to be properly passed to this function - // once the results view is in use. + const trainingErrorEval = await loadEvalData({ isTraining: true, index, dependentVariable, - resultsField: 'ml', - predictionFieldName: undefined, + resultsField, + predictionFieldName, }); if (trainingErrorEval.success === true && trainingErrorEval.eval) { @@ -99,14 +111,21 @@ export const EvaluatePanel: FC = ({ jobId, index, dependentVariable }) => return ( - - - {i18n.translate('xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle', { - defaultMessage: 'Job ID {jobId}', - values: { jobId }, - })} - - + + + + + {i18n.translate('xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle', { + defaultMessage: 'Regression job ID {jobId}', + values: { jobId: jobConfig.id }, + })} + + + + + {getTaskStateBadge(jobStatus)} + + diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx index f6cb010c3f040f..7beea07f9502db 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx @@ -4,25 +4,107 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, Fragment } from 'react'; -// import { i18n } from '@kbn/i18n'; -import { EuiSpacer } from '@elastic/eui'; - +import React, { FC, Fragment, useState, useEffect } from 'react'; +import { EuiCallOut, EuiLoadingSpinner, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ml } from '../../../../../services/ml_api_service'; +import { DataFrameAnalyticsConfig } from '../../../../common'; import { EvaluatePanel } from './evaluate_panel'; -// import { ResultsTable } from './results_table'; +import { ResultsTable } from './results_table'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; + +interface GetDataFrameAnalyticsResponse { + count: number; + data_frame_analytics: DataFrameAnalyticsConfig[]; +} + +const LoadingPanel: FC = () => ( + + + +); + +export const ExplorationTitle: React.SFC<{ jobId: string }> = ({ jobId }) => ( + + + {i18n.translate('xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle', { + defaultMessage: 'Regression job ID {jobId}', + values: { jobId }, + })} + + +); interface Props { jobId: string; - destIndex: string; - dependentVariable: string; + jobStatus: DATA_FRAME_TASK_STATE; } -export const RegressionExploration: FC = ({ jobId, destIndex, dependentVariable }) => { +export const RegressionExploration: FC = ({ jobId, jobStatus }) => { + const [jobConfig, setJobConfig] = useState(undefined); + const [isLoadingJobConfig, setIsLoadingJobConfig] = useState(false); + const [jobConfigErrorMessage, setJobConfigErrorMessage] = useState(undefined); + + const loadJobConfig = async () => { + setIsLoadingJobConfig(true); + try { + const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( + jobId + ); + if ( + Array.isArray(analyticsConfigs.data_frame_analytics) && + analyticsConfigs.data_frame_analytics.length > 0 + ) { + setJobConfig(analyticsConfigs.data_frame_analytics[0]); + setIsLoadingJobConfig(false); + } + } catch (e) { + if (e.message !== undefined) { + setJobConfigErrorMessage(e.message); + } else { + setJobConfigErrorMessage(JSON.stringify(e)); + } + setIsLoadingJobConfig(false); + } + }; + + useEffect(() => { + loadJobConfig(); + }, []); + + if (jobConfigErrorMessage !== undefined) { + return ( + + + + +

    {jobConfigErrorMessage}

    +
    +
    + ); + } + return ( - + {isLoadingJobConfig === true && jobConfig === undefined && } + {isLoadingJobConfig === false && jobConfig !== undefined && ( + + )} - {/* */} + {isLoadingJobConfig === true && jobConfig === undefined && } + {isLoadingJobConfig === false && jobConfig !== undefined && ( + + )} ); }; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx new file mode 100644 index 00000000000000..5ba3b8ed45939b --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx @@ -0,0 +1,467 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, FC, useEffect, useState } from 'react'; +import moment from 'moment-timezone'; + +import { i18n } from '@kbn/i18n'; +import { + EuiBadge, + EuiButtonIcon, + EuiCallOut, + EuiCheckbox, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPanel, + EuiPopover, + EuiPopoverTitle, + EuiProgress, + EuiSpacer, + EuiText, + EuiToolTip, + Query, +} from '@elastic/eui'; + +import { Query as QueryType } from '../../../analytics_management/components/analytics_list/common'; + +import { + ColumnType, + MlInMemoryTableBasic, + OnTableChangeArg, + SortingPropType, + SORT_DIRECTION, +} from '../../../../../components/ml_in_memory_table'; + +import { formatHumanReadableDateTimeSeconds } from '../../../../../util/date_utils'; +import { SavedSearchQuery } from '../../../../../contexts/kibana'; + +import { + sortRegressionResultsColumns, + sortRegressionResultsFields, + toggleSelectedField, + DataFrameAnalyticsConfig, + EsFieldName, + EsDoc, + MAX_COLUMNS, + getPredictedFieldName, + INDEX_STATUS, + SEARCH_SIZE, +} from '../../../../common'; +import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; +import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; + +import { useExploreData, defaultSearchQuery } from './use_explore_data'; +import { ExplorationTitle } from './regression_exploration'; + +const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; + +interface Props { + jobConfig: DataFrameAnalyticsConfig; + jobStatus: DATA_FRAME_TASK_STATE; +} + +export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => { + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(25); + const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); + const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); + const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); + const [searchError, setSearchError] = useState(undefined); + const [searchString, setSearchString] = useState(undefined); + + function toggleColumnsPopover() { + setColumnsPopoverVisible(!isColumnsPopoverVisible); + } + + function closeColumnsPopover() { + setColumnsPopoverVisible(false); + } + + function toggleColumn(column: EsFieldName) { + if (tableItems.length > 0 && jobConfig !== undefined) { + // spread to a new array otherwise the component wouldn't re-render + setSelectedFields([...toggleSelectedField(selectedFields, column)]); + } + } + + const { + errorMessage, + loadExploreData, + sortField, + sortDirection, + status, + tableItems, + } = useExploreData(jobConfig, selectedFields, setSelectedFields); + + let docFields: EsFieldName[] = []; + let docFieldsCount = 0; + if (tableItems.length > 0) { + docFields = Object.keys(tableItems[0]); + docFields.sort((a, b) => sortRegressionResultsFields(a, b, jobConfig)); + docFieldsCount = docFields.length; + } + + const columns: ColumnType[] = []; + + if (jobConfig !== undefined && selectedFields.length > 0 && tableItems.length > 0) { + columns.push( + ...selectedFields.sort(sortRegressionResultsColumns(tableItems[0], jobConfig)).map(k => { + const column: ColumnType = { + field: k, + name: k, + sortable: true, + truncateText: true, + }; + + const render = (d: any, fullItem: EsDoc) => { + if (Array.isArray(d) && d.every(item => typeof item === 'string')) { + // If the cells data is an array of strings, return as a comma separated list. + // The list will get limited to 5 items with `…` at the end if there's more in the original array. + return `${d.slice(0, 5).join(', ')}${d.length > 5 ? ', …' : ''}`; + } else if (Array.isArray(d)) { + // If the cells data is an array of e.g. objects, display a 'array' badge with a + // tooltip that explains that this type of field is not supported in this table. + return ( + + + {i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.indexArrayBadgeContent', + { + defaultMessage: 'array', + } + )} + + + ); + } else if (typeof d === 'object' && d !== null) { + // If the cells data is an object, display a 'object' badge with a + // tooltip that explains that this type of field is not supported in this table. + return ( + + + {i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.indexObjectBadgeContent', + { + defaultMessage: 'object', + } + )} + + + ); + } + + return d; + }; + + let columnType; + + if (tableItems.length > 0) { + columnType = typeof tableItems[0][k]; + } + + if (typeof columnType !== 'undefined') { + switch (columnType) { + case 'boolean': + column.dataType = 'boolean'; + break; + case 'Date': + column.align = 'right'; + column.render = (d: any) => + formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); + break; + case 'number': + column.dataType = 'number'; + column.render = render; + break; + default: + column.render = render; + break; + } + } else { + column.render = render; + } + + return column; + }) + ); + } + + useEffect(() => { + if (jobConfig !== undefined) { + const predictedFieldName = getPredictedFieldName( + jobConfig.dest.results_field, + jobConfig.analysis + ); + const predictedFieldSelected = selectedFields.includes(predictedFieldName); + + const field = predictedFieldSelected ? predictedFieldName : selectedFields[0]; + const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; + loadExploreData({ field, direction, searchQuery }); + } + }, [JSON.stringify(searchQuery)]); + + useEffect(() => { + // by default set the sorting to descending on the prediction field (`_prediction`). + // if that's not available sort ascending on the first column. + // also check if the current sorting field is still available. + if (jobConfig !== undefined && columns.length > 0 && !selectedFields.includes(sortField)) { + const predictedFieldName = getPredictedFieldName( + jobConfig.dest.results_field, + jobConfig.analysis + ); + const predictedFieldSelected = selectedFields.includes(predictedFieldName); + + const field = predictedFieldSelected ? predictedFieldName : selectedFields[0]; + const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; + loadExploreData({ field, direction, searchQuery }); + } + }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); + + let sorting: SortingPropType = false; + let onTableChange; + + if (columns.length > 0 && sortField !== '' && sortField !== undefined) { + sorting = { + sort: { + field: sortField, + direction: sortDirection, + }, + }; + + onTableChange = ({ + page = { index: 0, size: 10 }, + sort = { field: sortField, direction: sortDirection }, + }: OnTableChangeArg) => { + const { index, size } = page; + setPageIndex(index); + setPageSize(size); + + if (sort.field !== sortField || sort.direction !== sortDirection) { + loadExploreData({ ...sort, searchQuery }); + } + }; + } + + const pagination = { + initialPageIndex: pageIndex, + initialPageSize: pageSize, + totalItemCount: tableItems.length, + pageSizeOptions: PAGE_SIZE_OPTIONS, + hidePerPageOptions: false, + }; + + const onQueryChange = ({ query, error }: { query: QueryType; error: any }) => { + if (error) { + setSearchError(error.message); + } else { + try { + const esQueryDsl = Query.toESQuery(query); + setSearchQuery(esQueryDsl); + setSearchString(query.text); + setSearchError(undefined); + } catch (e) { + setSearchError(e.toString()); + } + } + }; + + const search = { + onChange: onQueryChange, + defaultQuery: searchString, + box: { + incremental: false, + placeholder: i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder', + { + defaultMessage: 'E.g. avg>0.5', + } + ), + }, + filters: [ + { + type: 'field_value_toggle_group', + field: `${jobConfig.dest.results_field}.is_training`, + items: [ + { + value: false, + name: i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel', + { + defaultMessage: 'Testing', + } + ), + }, + { + value: true, + name: i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel', + { + defaultMessage: 'Training', + } + ), + }, + ], + }, + ], + }; + + if (jobConfig === undefined) { + return null; + } + + if (status === INDEX_STATUS.ERROR) { + return ( + + + + + + + {getTaskStateBadge(jobStatus)} + + + +

    {errorMessage}

    +
    +
    + ); + } + + return ( + + + + + + + + + {getTaskStateBadge(jobStatus)} + + + + + + + {docFieldsCount > MAX_COLUMNS && ( + + {i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.fieldSelection', + { + defaultMessage: + '{selectedFieldsLength, number} of {docFieldsCount, number} {docFieldsCount, plural, one {field} other {fields}} selected', + values: { selectedFieldsLength: selectedFields.length, docFieldsCount }, + } + )} + + )} + + + + + } + isOpen={isColumnsPopoverVisible} + closePopover={closeColumnsPopover} + ownFocus + > + + {i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle', + { + defaultMessage: 'Select fields', + } + )} + +
    + {docFields.map(d => ( + toggleColumn(d)} + disabled={selectedFields.includes(d) && selectedFields.length === 1} + /> + ))} +
    +
    +
    +
    +
    +
    +
    + {status === INDEX_STATUS.LOADING && } + {status !== INDEX_STATUS.LOADING && ( + + )} + {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( + + + + + + + + )} +
    + ); +}); diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts new file mode 100644 index 00000000000000..3e7266eb89474b --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts @@ -0,0 +1,163 @@ +/* + * 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. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState } from 'react'; + +import { SearchResponse } from 'elasticsearch'; + +import { SortDirection, SORT_DIRECTION } from '../../../../../components/ml_in_memory_table'; + +import { ml } from '../../../../../services/ml_api_service'; +import { getNestedProperty } from '../../../../../util/object_utils'; +import { SavedSearchQuery } from '../../../../../contexts/kibana'; + +import { + getDefaultRegressionFields, + getFlattenedFields, + DataFrameAnalyticsConfig, + EsFieldName, + getPredictedFieldName, + INDEX_STATUS, + SEARCH_SIZE, +} from '../../../../common'; + +export const defaultSearchQuery = { + match_all: {}, +}; + +type TableItem = Record; + +interface LoadExploreDataArg { + field: string; + direction: SortDirection; + searchQuery: SavedSearchQuery; +} +export interface UseExploreDataReturnType { + errorMessage: string; + loadExploreData: (arg: LoadExploreDataArg) => void; + sortField: EsFieldName; + sortDirection: SortDirection; + status: INDEX_STATUS; + tableItems: TableItem[]; +} + +interface SearchQuery { + query: SavedSearchQuery; + sort?: any; +} + +export const useExploreData = ( + jobConfig: DataFrameAnalyticsConfig | undefined, + selectedFields: EsFieldName[], + setSelectedFields: React.Dispatch> +): UseExploreDataReturnType => { + const [errorMessage, setErrorMessage] = useState(''); + const [status, setStatus] = useState(INDEX_STATUS.UNUSED); + const [tableItems, setTableItems] = useState([]); + const [sortField, setSortField] = useState(''); + const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); + + const loadExploreData = async ({ field, direction, searchQuery }: LoadExploreDataArg) => { + if (jobConfig !== undefined) { + setErrorMessage(''); + setStatus(INDEX_STATUS.LOADING); + + try { + const resultsField = jobConfig.dest.results_field; + const body: SearchQuery = { + query: searchQuery, + }; + + if (field !== undefined) { + body.sort = [ + { + [field]: { + order: direction, + }, + }, + ]; + } + + const resp: SearchResponse = await ml.esSearch({ + index: jobConfig.dest.index, + size: SEARCH_SIZE, + body, + }); + + setSortField(field); + setSortDirection(direction); + + const docs = resp.hits.hits; + + if (docs.length === 0) { + setTableItems([]); + setStatus(INDEX_STATUS.LOADED); + return; + } + + if (selectedFields.length === 0) { + const newSelectedFields = getDefaultRegressionFields(docs, jobConfig); + setSelectedFields(newSelectedFields); + } + + // Create a version of the doc's source with flattened field names. + // This avoids confusion later on if a field name has dots in its name + // or is a nested fields when displaying it via EuiInMemoryTable. + const flattenedFields = getFlattenedFields(docs[0]._source, resultsField); + const transformedTableItems = docs.map(doc => { + const item: TableItem = {}; + flattenedFields.forEach(ff => { + item[ff] = getNestedProperty(doc._source, ff); + if (item[ff] === undefined) { + // If the attribute is undefined, it means it was not a nested property + // but had dots in its actual name. This selects the property by its + // full name and assigns it to `item[ff]`. + item[ff] = doc._source[`"${ff}"`]; + } + if (item[ff] === undefined) { + const parts = ff.split('.'); + if (parts[0] === resultsField && parts.length >= 2) { + parts.shift(); + if (doc._source[resultsField] !== undefined) { + item[ff] = doc._source[resultsField][parts.join('.')]; + } + } + } + }); + return item; + }); + + setTableItems(transformedTableItems); + setStatus(INDEX_STATUS.LOADED); + } catch (e) { + if (e.message !== undefined) { + setErrorMessage(e.message); + } else { + setErrorMessage(JSON.stringify(e)); + } + setTableItems([]); + setStatus(INDEX_STATUS.ERROR); + } + } + }; + + useEffect(() => { + if (jobConfig !== undefined) { + loadExploreData({ + field: getPredictedFieldName(jobConfig.dest.results_field, jobConfig.analysis), + direction: SORT_DIRECTION.DESC, + searchQuery: defaultSearchQuery, + }); + } + }, [jobConfig && jobConfig.id]); + + return { errorMessage, loadExploreData, sortField, sortDirection, status, tableItems }; +}; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx index ca448aaa7b9388..4a28e0b0b881ee 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx @@ -53,8 +53,7 @@ module.directive('mlDataFrameAnalyticsExploration', ($injector: InjectorService) , diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/page.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/page.tsx index 0bbc3137041742..b3d13db0a35505 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/page.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/page.tsx @@ -26,13 +26,13 @@ import { Exploration } from './components/exploration'; import { RegressionExploration } from './components/regression_exploration'; import { ANALYSIS_CONFIG_TYPE } from '../../common/analytics'; +import { DATA_FRAME_TASK_STATE } from '../analytics_management/components/analytics_list/common'; export const Page: FC<{ jobId: string; - analysisType: string; - destIndex: string; - depVar: string; -}> = ({ jobId, analysisType, destIndex, depVar }) => ( + analysisType: ANALYSIS_CONFIG_TYPE; + jobStatus: DATA_FRAME_TASK_STATE; +}> = ({ jobId, analysisType, jobStatus }) => ( @@ -66,9 +66,11 @@ export const Page: FC<{ - {analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && } + {analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && ( + + )} {analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION && ( - + )} diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index 7ad86c5d380cab..5e5283f9e6c49b 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -13,7 +13,7 @@ import { createPermissionFailureMessage, } from '../../../../../privilege/check_privilege'; -import { isOutlierAnalysis, getAnalysisType, getDependentVar } from '../../../../common/analytics'; +import { getAnalysisType } from '../../../../common/analytics'; import { getResultsUrl, isDataFrameAnalyticsRunning, DataFrameAnalyticsListRow } from './common'; import { stopAnalytics } from '../../services/analytics_service'; @@ -25,14 +25,11 @@ export const AnalyticsViewAction = { isPrimary: true, render: (item: DataFrameAnalyticsListRow) => { const analysisType = getAnalysisType(item.config.analysis); - const destIndex = item.config.dest.index; - const dependentVariable = getDependentVar(item.config.analysis); + const jobStatus = item.stats.state; - const url = getResultsUrl(item.id, analysisType, destIndex, dependentVariable); - // Disable 'View' link for regression until results view is complete + const url = getResultsUrl(item.id, analysisType, jobStatus); return ( (window.location.href = url)} size="xs" color="text" diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts index 99d1889b265edc..098c239e4f6ba5 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts @@ -5,7 +5,6 @@ */ import { DataFrameAnalyticsId, DataFrameAnalyticsConfig } from '../../../../common'; -import { ANALYSIS_CONFIG_TYPE } from '../../../../common/analytics'; export enum DATA_FRAME_TASK_STATE { ANALYZING = 'analyzing', @@ -118,17 +117,6 @@ export function isCompletedAnalyticsJob(stats: DataFrameAnalyticsStats) { return stats.state === DATA_FRAME_TASK_STATE.STOPPED && progress === 100; } -export function getResultsUrl( - jobId: string, - analysisType: string, - destIndex: string = '', - dependentVariable: string = '' -) { - const destIndexParam = `,destIndex:${destIndex}`; - const depVarParam = `,depVar:${dependentVariable}`; - const isRegression = analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION; - - return `ml#/data_frame_analytics/exploration?_g=(ml:(jobId:${jobId},analysisType:${analysisType}${ - isRegression && destIndex !== '' ? destIndexParam : '' - }${isRegression && dependentVariable !== '' ? depVarParam : ''}))`; +export function getResultsUrl(jobId: string, analysisType: string, status: DATA_FRAME_TASK_STATE) { + return `ml#/data_frame_analytics/exploration?_g=(ml:(jobId:${jobId},analysisType:${analysisType},jobStatus:${status}))`; } diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/data_loader/data_loader.ts b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/data_loader/data_loader.ts index 464be8a9157aec..f0bb998a276145 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/data_loader/data_loader.ts +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/data_loader/data_loader.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { decorateQuery, luceneStringToDsl } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { toastNotifications } from 'ui/notify'; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/edit_job.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/edit_job.tsx new file mode 100644 index 00000000000000..7ec8cddfe3ed5d --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/edit_job.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useEffect, useState } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiForm, + EuiFormRow, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ModuleJobUI } from '../page'; +import { usePartialState } from '../../../../components/custom_hooks'; +import { composeValidators, maxLengthValidator } from '../../../../../common/util/validators'; +import { isJobIdValid } from '../../../../../common/util/job_utils'; +import { JOB_ID_MAX_LENGTH } from '../../../../../common/constants/validation'; +import { JobGroupsInput } from '../../common/components'; +import { JobOverride } from '../../../../../common/types/modules'; + +interface EditJobProps { + job: ModuleJobUI; + jobOverride: JobOverride | undefined; + existingGroupIds: string[]; + onClose: (job: JobOverride | null) => void; +} + +/** + * Edit job flyout for overriding job configuration. + */ +export const EditJob: FC = ({ job, jobOverride, existingGroupIds, onClose }) => { + const [formState, setFormState] = usePartialState({ + jobGroups: (jobOverride && jobOverride.groups) || job.config.groups, + }); + const [validationResult, setValidationResult] = useState>({}); + + const groupValidator = composeValidators( + (value: string) => (isJobIdValid(value) ? null : { pattern: true }), + maxLengthValidator(JOB_ID_MAX_LENGTH) + ); + + const handleValidation = () => { + const jobGroupsValidationResult = formState.jobGroups + .map(group => groupValidator(group)) + .filter(result => result !== null); + + setValidationResult({ + jobGroups: jobGroupsValidationResult, + formValid: jobGroupsValidationResult.length === 0, + }); + }; + + useEffect(() => { + handleValidation(); + }, [formState.jobGroups]); + + const onSave = () => { + const result: JobOverride = { + job_id: job.id, + groups: formState.jobGroups, + }; + onClose(result); + }; + + return ( + onClose(null)}> + + +

    + +

    +
    +
    + + + + + { + setFormState({ + jobGroups: value, + }); + }} + validation={{ + valid: !validationResult.jobGroups || validationResult.jobGroups.length === 0, + message: ( + + ), + }} + /> + + + + + + + + onClose(null)} flush="left"> + + + + + onSave()} fill disabled={!validationResult.formValid}> + + + + + +
    + ); +}; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_item.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_item.tsx new file mode 100644 index 00000000000000..ace8409734b742 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_item.tsx @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, memo } from 'react'; +import { + EuiBadge, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLoadingSpinner, + EuiText, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ModuleJobUI } from '../page'; +import { SETUP_RESULTS_WIDTH } from './module_jobs'; +import { tabColor } from '../../../../../common/util/group_color_utils'; +import { JobOverride } from '../../../../../common/types/modules'; + +interface JobItemProps { + job: ModuleJobUI; + jobPrefix: string; + jobOverride: JobOverride | undefined; + isSaving: boolean; + onEditRequest: (job: ModuleJobUI) => void; +} + +export const JobItem: FC = memo( + ({ job, jobOverride, isSaving, jobPrefix, onEditRequest }) => { + const { + id, + config: { description, groups }, + datafeedResult, + setupResult, + } = job; + + const jobGroups = (jobOverride && jobOverride.groups) || groups; + + return ( + + + + + + {jobPrefix} + {id} + + + + + } + > + onEditRequest(job)} + /> + + + + + + {description} + + + + {jobGroups.map(group => ( + + {group} + + ))} + + + {setupResult && setupResult.error && ( + + {setupResult.error.msg} + + )} + + {datafeedResult && datafeedResult.error && ( + + {datafeedResult.error.msg} + + )} + + + {isSaving && } + {setupResult && datafeedResult && ( + + + + + + + + + + + + + + )} + + + ); + } +); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx index 617f6b31e7e537..bab45a7d77a3de 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx @@ -27,9 +27,8 @@ import { patternValidator, } from '../../../../../common/util/validators'; import { JOB_ID_MAX_LENGTH } from '../../../../../common/constants/validation'; -import { isJobIdValid } from '../../../../../common/util/job_utils'; import { usePartialState } from '../../../../components/custom_hooks'; -import { JobGroupsInput, TimeRangePicker, TimeRange } from '../../common/components'; +import { TimeRange, TimeRangePicker } from '../../common/components'; export interface JobSettingsFormValues { jobPrefix: string; @@ -37,15 +36,12 @@ export interface JobSettingsFormValues { useFullIndexData: boolean; timeRange: TimeRange; useDedicatedIndex: boolean; - jobGroups: string[]; } interface JobSettingsFormProps { saveState: SAVE_STATE; onSubmit: (values: JobSettingsFormValues) => any; onChange: (values: JobSettingsFormValues) => any; - jobGroups: string[]; - existingGroupIds: string[]; jobs: ModuleJobUI[]; } @@ -53,9 +49,7 @@ export const JobSettingsForm: FC = ({ onSubmit, onChange, saveState, - existingGroupIds, jobs, - jobGroups, }) => { const { from, to } = getTimeFilterRange(); const { currentIndexPattern: indexPattern } = useKibanaContext(); @@ -64,10 +58,6 @@ export const JobSettingsForm: FC = ({ patternValidator(/^([a-z0-9]+[a-z0-9\-_]*)?$/), maxLengthValidator(JOB_ID_MAX_LENGTH - Math.max(...jobs.map(({ id }) => id.length))) ); - const groupValidator = composeValidators( - (value: string) => (isJobIdValid(value) ? null : { pattern: true }), - maxLengthValidator(JOB_ID_MAX_LENGTH) - ); const [formState, setFormState] = usePartialState({ jobPrefix: '', @@ -78,7 +68,6 @@ export const JobSettingsForm: FC = ({ end: to, }, useDedicatedIndex: false, - jobGroups: [] as string[], }); const [validationResult, setValidationResult] = useState>({}); @@ -90,29 +79,21 @@ export const JobSettingsForm: FC = ({ const handleValidation = () => { const jobPrefixValidationResult = jobPrefixValidator(formState.jobPrefix); - const jobGroupsValidationResult = formState.jobGroups - .map(group => groupValidator(group)) - .filter(result => result !== null); setValidationResult({ jobPrefix: jobPrefixValidationResult, - jobGroups: jobGroupsValidationResult, - formValid: !jobPrefixValidationResult && jobGroupsValidationResult.length === 0, + formValid: !jobPrefixValidationResult, }); }; useEffect(() => { handleValidation(); - }, [formState.jobPrefix, formState.jobGroups]); + }, [formState.jobPrefix]); useEffect(() => { onChange(formState); }, [formState]); - useEffect(() => { - setFormState({ jobGroups }); - }, [jobGroups]); - return ( <> @@ -174,24 +155,6 @@ export const JobSettingsForm: FC = ({ /> - { - setFormState({ - jobGroups: value, - }); - }} - validation={{ - valid: !validationResult.jobGroups || validationResult.jobGroups.length === 0, - message: ( - - ), - }} - /> void; } -const SETUP_RESULTS_WIDTH = '200px'; +export const SETUP_RESULTS_WIDTH = '200px'; -export const ModuleJobs: FC = ({ jobs, jobPrefix, saveState }) => { +export const ModuleJobs: FC = ({ + jobs, + jobPrefix, + jobOverrides, + saveState, + existingGroupIds, + onJobOverridesChange, +}) => { const isSaving = saveState === SAVE_STATE.SAVING; + + const [jobToEdit, setJobToEdit] = useState(null); + + const onFlyoutClose = (result: JobOverride | null) => { + setJobToEdit(null); + + if (result === null) { + return; + } + + onJobOverridesChange(result); + }; + + const getJobOverride = (job: ModuleJobUI): JobOverride | undefined => { + return jobOverrides[job.id]; + }; + + const editJobFlyout = + jobToEdit !== null ? ( + + ) : null; + return ( <> @@ -70,109 +107,26 @@ export const ModuleJobs: FC = ({ jobs, jobPrefix, saveState }) )}
      - {jobs.map(({ id, config: { description }, setupResult, datafeedResult }, i) => ( -
    • - + {jobs.map((job, i) => ( +
    • + - - {jobPrefix} - {id} - - - - {description} - - - {setupResult && setupResult.error && ( - - {setupResult.error.msg} - - )} - - {datafeedResult && datafeedResult.error && ( - - {datafeedResult.error.msg} - - )} - - - {isSaving && } - {setupResult && datafeedResult && ( - - - - - - - - - - - - - - )} + setJobToEdit(job)} + /> + {i < jobs.length - 1 && }
    • ))}
    + + {editJobFlyout} ); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/page.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/page.tsx index 2c7600dcb99b20..f9a5230ef17d9f 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/page.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/page.tsx @@ -21,12 +21,13 @@ import { EuiPanel, } from '@elastic/eui'; import { toastNotifications } from 'ui/notify'; -import { merge, flatten } from 'lodash'; +import { merge } from 'lodash'; import { ml } from '../../../services/ml_api_service'; import { useKibanaContext } from '../../../contexts/kibana'; import { DatafeedResponse, DataRecognizerConfigResponse, + JobOverride, JobResponse, KibanaObject, KibanaObjectResponse, @@ -40,6 +41,7 @@ import { ModuleJobs } from './components/module_jobs'; import { checkForSavedObjects } from './resolvers'; import { JobSettingsForm, JobSettingsFormValues } from './components/job_settings_form'; import { TimeRange } from '../common/components'; +import { JobId } from '../common/job_creator/configs'; export interface ModuleJobUI extends ModuleJob { datafeedResult?: DatafeedResponse; @@ -57,6 +59,8 @@ interface PageProps { existingGroupIds: string[]; } +export type JobOverrides = Record; + export enum SAVE_STATE { NOT_SAVED, SAVING, @@ -69,7 +73,7 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { // #region State const [jobPrefix, setJobPrefix] = useState(''); const [jobs, setJobs] = useState([]); - const [jobGroups, setJobGroups] = useState([]); + const [jobOverrides, setJobOverrides] = useState({}); const [kibanaObjects, setKibanaObjects] = useState({}); const [saveState, setSaveState] = useState(SAVE_STATE.NOT_SAVED); const [resultsUrl, setResultsUrl] = useState(''); @@ -93,6 +97,9 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { const displayQueryWarning = savedSearch.id !== undefined; const tempQuery = savedSearch.id === undefined ? undefined : combinedQuery; + /** + * Loads recognizer module configuration. + */ const loadModule = async () => { try { const response: Module = await ml.getDataRecognizerModule({ moduleId }); @@ -101,9 +108,6 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { const kibanaObjectsResult = await checkForSavedObjects(response.kibana as KibanaObjects); setKibanaObjects(kibanaObjectsResult); - setJobGroups([ - ...new Set(flatten(response.jobs.map(({ config: { groups = [] } }) => groups))), - ]); setSaveState(SAVE_STATE.NOT_SAVED); } catch (e) { // eslint-disable-next-line no-console @@ -134,11 +138,13 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { loadModule(); }, []); + /** + * Sets up recognizer module configuration. + */ const save = async (formValues: JobSettingsFormValues) => { setSaveState(SAVE_STATE.SAVING); const { jobPrefix: resultJobPrefix, - jobGroups: resultJobGroups, startDatafeedAfterSave, useDedicatedIndex, useFullIndexData, @@ -148,14 +154,17 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { const resultTimeRange = await getTimeRange(useFullIndexData, timeRange); try { + let jobOverridesPayload: JobOverride[] | null = Object.values(jobOverrides); + jobOverridesPayload = jobOverridesPayload.length > 0 ? jobOverridesPayload : null; + const response: DataRecognizerConfigResponse = await ml.setupDataRecognizerConfig({ moduleId, prefix: resultJobPrefix, - groups: resultJobGroups, query: tempQuery, indexPatternName: indexPattern.title, useDedicatedIndex, startDatafeed: startDatafeedAfterSave, + ...(jobOverridesPayload !== null ? { jobOverrides: jobOverridesPayload } : {}), ...resultTimeRange, }); const { datafeeds: datafeedsResponse, jobs: jobsResponse, kibana: kibanaResponse } = response; @@ -208,6 +217,13 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { } }; + const onJobOverridesChange = (job: JobOverride) => { + setJobOverrides({ + ...jobOverrides, + [job.job_id as string]: job, + }); + }; + const isFormVisible = [SAVE_STATE.NOT_SAVED, SAVE_STATE.SAVING].includes(saveState); return ( @@ -271,10 +287,8 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { onChange={formValues => { setJobPrefix(formValues.jobPrefix); }} - existingGroupIds={existingGroupIds} saveState={saveState} jobs={jobs} - jobGroups={jobGroups} /> )} = ({ moduleId, existingGroupIds }) => {
    - + {Object.keys(kibanaObjects).length > 0 && ( <> diff --git a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js index c6a60a9eff7daa..94c79fe470236d 100644 --- a/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js +++ b/x-pack/legacy/plugins/ml/public/services/ml_api_service/index.js @@ -292,7 +292,8 @@ export const ml = { 'useDedicatedIndex', 'startDatafeed', 'start', - 'end' + 'end', + 'jobOverrides', ]); return http({ diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js b/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js index 2ed0f9b2db734e..af5039fafdc604 100644 --- a/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/__tests__/data_recognizer.js @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - import expect from '@kbn/expect'; import { DataRecognizer } from '../data_recognizer'; @@ -36,10 +34,68 @@ describe('ML - data recognizer', () => { expect(ids.join()).to.equal(moduleIds.join()); }); - it('getModule - load a single module', async () => { const module = await dr.getModule(moduleIds[0]); expect(module.id).to.equal(moduleIds[0]); }); + describe('jobOverrides', () => { + it('should apply job overrides correctly', () => { + // arrange + const prefix = 'pre-'; + const testJobId = 'test-job'; + const moduleConfig = { + jobs: [ + { + id: `${prefix}${testJobId}`, + config: { + groups: ['nginx'], + analysis_config: { + bucket_span: '1h' + }, + analysis_limits: { + model_memory_limit: '256mb', + influencers: [ + 'region' + ] + }, + calendars: ['calendar-1'], + } + }, + ], + }; + const jobOverrides = [ + { + analysis_limits: { + model_memory_limit: '512mb', + influencers: [], + } + }, + { + job_id: testJobId, + groups: [], + }, + ]; + // act + dr.applyJobConfigOverrides(moduleConfig, jobOverrides, prefix); + // assert + expect(moduleConfig.jobs).to.eql([ + { + config: { + analysis_config: { + bucket_span: '1h' + }, + analysis_limits: { + model_memory_limit: '512mb', + influencers: [], + }, + groups: [], + calendars: ['calendar-1'], + }, + id: 'pre-test-job' + } + ]); + }); + }); }); + diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.js b/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.js index 0b0d2c5adfea09..b5c897a5a3cc98 100644 --- a/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.js +++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.js @@ -811,45 +811,78 @@ export class DataRecognizer { } applyJobConfigOverrides(moduleConfig, jobOverrides, jobPrefix = '') { - if(jobOverrides !== undefined && jobOverrides !== null) { - if (typeof jobOverrides !== 'object') { - throw Boom.badRequest( - `Incompatible jobOverrides type (${typeof jobOverrides}). It needs to be an object or array of objects.` - ); + if (jobOverrides === undefined || jobOverrides === null) { + return; + } + + if (typeof jobOverrides !== 'object') { + throw Boom.badRequest( + `Incompatible jobOverrides type (${typeof jobOverrides}). It needs to be an object or array of objects.` + ); + } + + // jobOverrides could be a single object or an array of objects. + // if single, convert to an array + const overrides = Array.isArray(jobOverrides) ? jobOverrides : [jobOverrides]; + const { jobs } = moduleConfig; + + // separate all the overrides. + // the overrides which don't contain a job id will be applied to all jobs in the module + const generalOverrides = []; + const jobSpecificOverrides = []; + + overrides.forEach(override => { + if (override.job_id === undefined) { + generalOverrides.push(override); + } else { + jobSpecificOverrides.push(override); } + }); - // jobOverrides could be a single object or an array of objects. - // if single, convert to an array - const overrides = Array.isArray(jobOverrides) ? jobOverrides : [jobOverrides]; - const { jobs } = moduleConfig; + function processArrayValues(source, update) { + if (typeof source !== 'object' || typeof update !== 'object') { + return; + } - // separate all the overrides. - // the overrides which don't contain a job id will be applied to all jobs in the module - const generalOverrides = []; - const jobSpecificOverrides = []; - overrides.forEach(o => { - if (o.job_id === undefined) { - generalOverrides.push(o); + Object.keys(source).forEach(key => { + const sourceValue = source[key]; + const updateValue = update[key]; + + if ( + typeof sourceValue !== 'object' || + sourceValue === null || + typeof updateValue !== 'object' || + updateValue === null + ) { + return; + } + + if (Array.isArray(sourceValue) && Array.isArray(updateValue)) { + source[key] = updateValue; } else { - jobSpecificOverrides.push(o); + processArrayValues(sourceValue, updateValue); } }); + } - generalOverrides.forEach(o => { - jobs.forEach(({ config }) => merge(config, o)); + generalOverrides.forEach(generalOverride => { + jobs.forEach(job => { + merge(job.config, generalOverride); + processArrayValues(job.config, generalOverride); }); + }); - jobSpecificOverrides.forEach(o => { - // for each override, find the relevant job. - // note, the job id already has the prefix prepended to it - const job = jobs.find(j => j.id === `${jobPrefix}${o.job_id}`); - if (job !== undefined) { - // delete the job_id in the override as this shouldn't be overridden - delete o.job_id; - merge(job.config, o); - } - }); - } + jobSpecificOverrides.forEach(jobSpecificOverride => { + // for each override, find the relevant job. + // note, the job id already has the prefix prepended to it + const job = jobs.find(j => j.id === `${jobPrefix}${jobSpecificOverride.job_id}`); + if (job !== undefined) { + // delete the job_id in the override as this shouldn't be overridden + delete jobSpecificOverride.job_id; + merge(job.config, jobSpecificOverride); + processArrayValues(job.config, jobSpecificOverride); + } + }); } applyDatafeedConfigOverrides(moduleConfig, datafeedOverrides, jobPrefix = '') { diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js b/x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js index e50e49eec9f6eb..f2f27499d113c3 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js +++ b/x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { Fragment, useState } from 'react'; import PropTypes from 'prop-types'; import { EuiSpacer, @@ -43,7 +43,7 @@ function NoDataMessage(props) { export function NoData(props) { const [isLoading, setIsLoading] = useState(false); - const [useInternalCollection, setUseInternalCollection] = useState(false); + const [useInternalCollection, setUseInternalCollection] = useState(props.isOnCloud); async function startSetup() { setIsLoading(true); @@ -64,15 +64,19 @@ export function NoData(props) { - - setUseInternalCollection(false)}> - - - - + { !props.isOnCloud ? ( + + + setUseInternalCollection(false)}> + + + + + + ) : null } diff --git a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.js b/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.js index 3e7d182f1514c5..7da419719e70c7 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.js +++ b/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.js @@ -7,6 +7,7 @@ import { ajaxErrorHandlersProvider } from './ajax_error_handler'; import { get, contains } from 'lodash'; import chrome from 'ui/chrome'; +import { i18n } from '@kbn/i18n'; function isOnPage(hash) { return contains(window.location.hash, hash); @@ -80,7 +81,7 @@ export const updateSetupModeData = async (uuid, fetchWithoutClusterUuid = false) const oldData = setupModeState.data; const data = await fetchCollectionData(uuid, fetchWithoutClusterUuid); setupModeState.data = data; - if (get(data, '_meta.isOnCloud', false)) { + if (chrome.getInjected('isOnCloud')) { return toggleSetupMode(false); // eslint-disable-line no-use-before-define } notifySetupModeDataChange(oldData); @@ -139,15 +140,17 @@ export const setSetupModeMenuItem = () => { } const globalState = angularState.injector.get('globalState'); - const navItems = globalState.inSetupMode - ? [] - : [{ + const navItems = []; + if (!globalState.inSetupMode && !chrome.getInjected('isOnCloud')) { + navItems.push({ id: 'enter', - label: 'Enter Setup Mode', - description: 'Enter setup', + label: i18n.translate('xpack.monitoring.setupMode.enter', { + defaultMessage: 'Enter Setup Mode' + }), run: () => toggleSetupMode(true), testId: 'enterSetupMode' - }]; + }); + } angularState.scope.topNavMenu = [...navItems]; // LOL angular diff --git a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js b/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js index b5878c7ec51818..4e3a8045048ae6 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js +++ b/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - toggleSetupMode, - initSetupModeState, - getSetupModeState, - updateSetupModeData, - setSetupModeMenuItem -} from './setup_mode'; +let toggleSetupMode; +let initSetupModeState; +let getSetupModeState; +let updateSetupModeData; +let setSetupModeMenuItem; jest.mock('./ajax_error_handler', () => ({ ajaxErrorHandlersProvider: err => { @@ -52,16 +50,31 @@ function waitForSetupModeData(action) { process.nextTick(action); } +function setModules() { + jest.resetModules(); + injectorModulesMock.globalState.inSetupMode = false; + + const setupMode = require('./setup_mode'); + toggleSetupMode = setupMode.toggleSetupMode; + initSetupModeState = setupMode.initSetupModeState; + getSetupModeState = setupMode.getSetupModeState; + updateSetupModeData = setupMode.updateSetupModeData; + setSetupModeMenuItem = setupMode.setSetupModeMenuItem; +} + describe('setup_mode', () => { - describe('setup', () => { - afterEach(async () => { - try { - toggleSetupMode(false); - } catch (err) { - // Do nothing... + beforeEach(async () => { + jest.doMock('ui/chrome', () => ({ + getInjected: (key) => { + if (key === 'isOnCloud') { + return false; + } } - }); + })); + setModules(); + }); + describe('setup', () => { it('should require angular state', async () => { let error; try { @@ -99,21 +112,25 @@ describe('setup_mode', () => { describe('in setup mode', () => { afterEach(async () => { data = {}; - toggleSetupMode(false); }); it('should enable it through clicking top nav item', async () => { initSetupModeState(angularStateMock.scope, angularStateMock.injector); + setSetupModeMenuItem(); + expect(injectorModulesMock.globalState.inSetupMode).toBe(false); await angularStateMock.scope.topNavMenu[0].run(); expect(injectorModulesMock.globalState.inSetupMode).toBe(true); }); it('should not fetch data if on cloud', async (done) => { - data = { - _meta: { - isOnCloud: true + jest.doMock('ui/chrome', () => ({ + getInjected: (key) => { + if (key === 'isOnCloud') { + return true; + } } - }; + })); + setModules(); initSetupModeState(angularStateMock.scope, angularStateMock.injector); await toggleSetupMode(true); waitForSetupModeData(() => { diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js b/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js index 0ecd6c83265fff..f364b70fd934fc 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js +++ b/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js @@ -5,6 +5,7 @@ */ import React from 'react'; +import chrome from 'ui/chrome'; import { ClusterSettingsChecker, NodeSettingsChecker, @@ -99,7 +100,12 @@ export class NoDataController extends MonitoringViewBaseController { this.renderReact( - + ); } diff --git a/x-pack/legacy/plugins/monitoring/server/lib/setup/collection/get_collection_status.js b/x-pack/legacy/plugins/monitoring/server/lib/setup/collection/get_collection_status.js index a49da8ba60200a..cd5781ccf53446 100644 --- a/x-pack/legacy/plugins/monitoring/server/lib/setup/collection/get_collection_status.js +++ b/x-pack/legacy/plugins/monitoring/server/lib/setup/collection/get_collection_status.js @@ -547,7 +547,6 @@ export const getCollectionStatus = async (req, indexPatterns, clusterUuid, nodeU status._meta = { secondsAgo: NUMBER_OF_SECONDS_AGO_TO_LOOK, liveClusterUuid, - isOnCloud: get(req.server.plugins, 'cloud.config.isCloudEnabled', false) }; return status; diff --git a/x-pack/legacy/plugins/monitoring/ui_exports.js b/x-pack/legacy/plugins/monitoring/ui_exports.js index a10b83086f738b..0976292be576b1 100644 --- a/x-pack/legacy/plugins/monitoring/ui_exports.js +++ b/x-pack/legacy/plugins/monitoring/ui_exports.js @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { resolve } from 'path'; /** @@ -28,7 +29,8 @@ export const getUiExports = () => ({ injectDefaultVars(server) { const config = server.config(); return { - monitoringUiEnabled: config.get('xpack.monitoring.ui.enabled') + monitoringUiEnabled: config.get('xpack.monitoring.ui.enabled'), + isOnCloud: get(server.plugins, 'cloud.config.isCloudEnabled', false) }; }, hacks: [ 'plugins/monitoring/hacks/toggle_app_link_in_nav' ], diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index 0c63def67bd042..30bbac61b02480 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -26,9 +26,8 @@ import { getElementPositionAndAttributes } from './get_element_position_data'; import { getScreenshots } from './get_screenshots'; import { skipTelemetry } from './skip_telemetry'; -// NOTE: Typescript does not throw an error if this interface has errors! interface ScreenshotResults { - timeRang: TimeRange; + timeRange: TimeRange; screenshots: Screenshot[]; } @@ -49,6 +48,7 @@ export function screenshotsObservableFactory(server: ServerFacade) { browserTimezone, }); + // @ts-ignore this needs to be refactored to use less random type declaration and instead rely on structures that work with inference return create$.pipe( mergeMap(({ driver$, exit$ }) => { const screenshot$ = driver$.pipe( diff --git a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index f16485537f53a6..ca26f7d41c12a8 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -174,8 +174,8 @@ export class HeadlessChromiumDriverFactory { } getBrowserLogger(page: Page): Rx.Observable { - return Rx.fromEvent(page as NodeJS.EventEmitter, 'console').pipe( - tap((line: ConsoleMessage) => { + return Rx.fromEvent(page, 'console').pipe( + tap(line => { if (line.type() === 'error') { this.logger.error(line.text(), ['headless-browser-console']); } else { @@ -185,14 +185,14 @@ export class HeadlessChromiumDriverFactory { ); } - getProcessLogger(browser: Browser): Rx.Observable { + getProcessLogger(browser: Browser) { const childProcess = browser.process(); // NOTE: The browser driver can not observe stdout and stderr of the child process // Puppeteer doesn't give a handle to the original ChildProcess object // See https://github.com/GoogleChrome/puppeteer/issues/1292#issuecomment-521470627 // just log closing of the process - const processClose$: Rx.Observable = Rx.fromEvent(childProcess, 'close').pipe( + const processClose$ = Rx.fromEvent(childProcess, 'close').pipe( tap(() => { this.logger.debug('child process closed', ['headless-browser-process']); }) @@ -201,17 +201,15 @@ export class HeadlessChromiumDriverFactory { return processClose$; // ideally, this would also merge with observers for stdout and stderr } - getPageExit(browser: Browser, page: Page): Rx.Observable { - const pageError$: Rx.Observable = Rx.fromEvent(page, 'error').pipe( - mergeMap((err: Error) => Rx.throwError(err)) - ); + getPageExit(browser: Browser, page: Page) { + const pageError$ = Rx.fromEvent(page, 'error').pipe(mergeMap(err => Rx.throwError(err))); - const uncaughtExceptionPageError$: Rx.Observable = Rx.fromEvent(page, 'pageerror').pipe( - mergeMap((err: Error) => Rx.throwError(err)) + const uncaughtExceptionPageError$ = Rx.fromEvent(page, 'pageerror').pipe( + mergeMap(err => Rx.throwError(err)) ); - const pageRequestFailed$: Rx.Observable = Rx.fromEvent(page, 'requestfailed').pipe( - mergeMap((req: PuppeteerRequest) => { + const pageRequestFailed$ = Rx.fromEvent(page, 'requestfailed').pipe( + mergeMap(req => { const failure = req.failure && req.failure(); if (failure) { return Rx.throwError( diff --git a/x-pack/legacy/plugins/searchprofiler/index.js b/x-pack/legacy/plugins/searchprofiler/index.js deleted file mode 100644 index 107edeb1f408df..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/index.js +++ /dev/null @@ -1,56 +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 { resolve } from 'path'; -import { profileRoute } from './server/routes/profile'; - -// License -import Boom from 'boom'; -import { checkLicense } from './server/lib/check_license'; -import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; - - -export const searchprofiler = (kibana) => { - return new kibana.Plugin({ - require: ['elasticsearch', 'xpack_main'], - id: 'searchprofiler', - configPrefix: 'xpack.searchprofiler', - publicDir: resolve(__dirname, 'public'), - - uiExports: { - devTools: ['plugins/searchprofiler/app'], - hacks: ['plugins/searchprofiler/register'], - home: ['plugins/searchprofiler/register_feature'], - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - }, - init: function (server) { - const thisPlugin = this; - const xpackMainPlugin = server.plugins.xpack_main; - mirrorPluginStatus(xpackMainPlugin, thisPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(thisPlugin.id).registerLicenseCheckResultsGenerator(checkLicense); - }); - - // Add server routes and initialize the plugin here - const commonRouteConfig = { - pre: [ - function forbidApiAccess() { - const licenseCheckResults = xpackMainPlugin.info.feature(thisPlugin.id).getLicenseCheckResults(); - if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) { - return null; - } else { - throw Boom.forbidden(licenseCheckResults.message); - } - } - ] - }; - profileRoute(server, commonRouteConfig); - } - - }); -}; diff --git a/x-pack/legacy/plugins/searchprofiler/index.ts b/x-pack/legacy/plugins/searchprofiler/index.ts new file mode 100644 index 00000000000000..5de6ba710235bc --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/index.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 { resolve } from 'path'; +import Boom from 'boom'; + +import { CoreSetup } from 'src/core/server'; +import { Server } from 'src/legacy/server/kbn_server'; +import { LegacySetup } from './server/np_ready/types'; +import { plugin } from './server/np_ready'; + +export const searchprofiler = (kibana: any) => { + const publicSrc = resolve(__dirname, 'public'); + + return new kibana.Plugin({ + require: ['elasticsearch', 'xpack_main'], + id: 'searchprofiler', + configPrefix: 'xpack.searchprofiler', + publicDir: publicSrc, + + uiExports: { + // NP Ready + devTools: [`${publicSrc}/legacy`], + styleSheetPaths: `${publicSrc}/np_ready/application/index.scss`, + // Legacy + hacks: ['plugins/searchprofiler/register'], + home: ['plugins/searchprofiler/register_feature'], + }, + init(server: Server) { + const serverPlugin = plugin(); + const thisPlugin = this; + + const commonRouteConfig = { + pre: [ + function forbidApiAccess() { + const licenseCheckResults = server.plugins.xpack_main.info + .feature(thisPlugin.id) + .getLicenseCheckResults(); + if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) { + return null; + } else { + throw Boom.forbidden(licenseCheckResults.message); + } + }, + ], + }; + + const legacySetup: LegacySetup = { + route: (args: Parameters[0]) => server.route(args), + plugins: { + __LEGACY: { + thisPlugin, + xpackMain: server.plugins.xpack_main, + elasticsearch: server.plugins.elasticsearch, + commonRouteConfig, + }, + }, + }; + serverPlugin.setup({} as CoreSetup, legacySetup); + }, + }); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/app.js b/x-pack/legacy/plugins/searchprofiler/public/app.js deleted file mode 100644 index 1c7598ab982bcb..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/app.js +++ /dev/null @@ -1,193 +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. - */ - - -// K5 imports -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import uiRoutes from 'ui/routes'; -import 'ui/capabilities/route_setup'; -import { toastNotifications } from 'ui/notify'; -import { formatAngularHttpError } from 'ui/notify/lib'; - -// License -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -// Our imports -import $ from 'jquery'; -import _ from 'lodash'; -import 'ace'; -import 'angular-ui-ace'; -import 'plugins/searchprofiler/directives'; -import './components/searchprofiler_tabs_directive'; -import { Range } from './range'; -import { nsToPretty } from 'plugins/searchprofiler/filters/ns_to_pretty'; -import { msToPretty } from 'plugins/searchprofiler/filters/ms_to_pretty'; -import { checkForParseErrors } from 'plugins/searchprofiler/app_util.js'; -import { initializeEditor } from 'plugins/searchprofiler/editor'; - -// Styles and templates -import 'ui/autoload/all'; -import template from './templates/index.html'; -import { defaultQuery } from './templates/default_query'; - -uiRoutes.when('/dev_tools/searchprofiler', { - template: template, - requireUICapability: 'dev_tools.show', - controller: $scope => { - $scope.registerLicenseLinkLabel = i18n.translate('xpack.searchProfiler.registerLicenseLinkLabel', - { defaultMessage: 'register a license' }); - $scope.trialLicense = i18n.translate('xpack.searchProfiler.trialLicenseTitle', - { defaultMessage: 'Trial' }); - $scope.basicLicense = i18n.translate('xpack.searchProfiler.basicLicenseTitle', - { defaultMessage: 'Basic' }); - $scope.goldLicense = i18n.translate('xpack.searchProfiler.goldLicenseTitle', - { defaultMessage: 'Gold' }); - $scope.platinumLicense = i18n.translate('xpack.searchProfiler.platinumLicenseTitle', - { defaultMessage: 'Platinum' }); - }, -}); - -uiModules - .get('app/searchprofiler', ['ui.ace']) - .controller('profileViz', profileVizController) - .filter('nsToPretty', () => nsToPretty) - .filter('msToPretty', () => msToPretty) - .factory('HighlightService', () => { - const service = { - details: null - }; - return service; - }); - -function profileVizController($scope, $timeout, $http, HighlightService) { - $scope.title = 'Search Profile'; - $scope.description = 'Search profiling and visualization'; - $scope.profileResponse = []; - $scope.highlight = HighlightService; - $scope.index = '_all'; - $scope.query = ''; - - // TODO this map controls which tab is active, but due to how - // the tab directive works, we cannot use a single variable to hold the state. - // Instead we have to map the tab name to true/false, and make sure only one - // state is active. This should be refactored if possible, as it could be trappy! - $scope.activeTab = { - search: true - }; - $scope.markers = []; - $scope.licenseEnabled = xpackInfo.get('features.searchprofiler.enableAppLink'); - - - const editor = initializeEditor({ - el: $('#SearchProfilerInput')[0], - licenseEnabled: $scope.licenseEnabled, - }); - - editor.on('change', () => { - // Do a safe apply/trigger digest - $timeout(() => { - $scope.query = editor.getValue(); - }); - }); - - editor.setValue(defaultQuery, 1); - - $scope.hasQuery = () => Boolean($scope.query); - - $scope.profile = () => { - const { query } = $scope; - if (!$scope.licenseEnabled) { - return; - } - // Reset right detail panel - $scope.resetHighlightPanel(); - let json = checkForParseErrors(query); - if (json.status === false) { - toastNotifications.addError(json.error, { - title: i18n.translate('xpack.searchProfiler.errorToastTitle', { - defaultMessage: 'JSON parse error', - }), - }); - return; - } - json = json.parsed; - - // If we can find the start of a profile JSON output, just try to render it - // without executing - if (json.profile && json.profile.shards) { - $scope.renderProfile(json.profile.shards); - } else { - // Otherwise it's (probably) a regular search, execute remotely - const requestBody = { query }; - if ($scope.index == null || $scope.index === '') { - requestBody.index = '_all'; - } else { - requestBody.index = $scope.index; - } - if (!$scope.type === '') { - requestBody.type = $scope.type; - } - $scope.executeRemoteQuery(requestBody); - } - }; - - $scope.executeRemoteQuery = requestBody => { - $http.post('../api/searchprofiler/profile', requestBody).then(resp => { - if (!resp.data.ok) { - toastNotifications.addDanger(resp.data.err.msg); - - try { - const regex = /line=([0-9]+) col=([0-9]+)/g; - const [ , row, column ] = regex.exec(resp.data.err.msg); - - $scope.markers.push($scope.ace.session.addMarker( - new Range(row - 1, 0, row - 1, column), 'errorMarker', 'fullLine')); - } catch (e) { - // Best attempt, not a big deal if we can't highlight the line - } - - return; - } - - $scope.renderProfile(resp.data.resp.profile.shards); - }).catch(reason => toastNotifications.addDanger(formatAngularHttpError(reason))); - }; - - $scope.renderProfile = data => { - for (const shard of data) { - shard.id = shard.id.match(/\[([^\]\[]*?)\]/g); - shard.id = _.map(shard.id, id => { - return id.replace('[', '').replace(']', ''); - }); - } - $scope.profileResponse = data; - - const hasAggregations = data[0].aggregations != null && data[0].aggregations.length > 0; - if (!hasAggregations) { - // No aggs, reset back to search panel - $scope.activateTab('search'); - } - }; - - $scope.activateTab = tab => { - // Reset right detail panel - $scope.resetHighlightPanel(); - // Reset active tab map - $scope.activeTab = {}; - if (tab === 'aggregations') { - $scope.activeTab.aggregations = true; - } else { - // Everything has a search, so default to this - $scope.activeTab.search = true; - } - }; - - $scope.resetHighlightPanel = () => { - $scope.highlight.details = null; - }; - -} diff --git a/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs.js b/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs.js deleted file mode 100644 index 5394e56cef70ad..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { - EuiTabs, - EuiTab -} from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -function hasSearch(profileResponse) { - const aggs = _.get(profileResponse, '[0].searches', []); - return aggs.length > 0; -} - -function hasAggregations(profileResponse) { - const aggs = _.get(profileResponse, '[0].aggregations', []); - return aggs.length > 0; -} - - -function handleClick(activateTab, tabName) { - activateTab(tabName); -} - -export function SearchProfilerTabs(props) { - return ( - - handleClick(props.activateTab, 'search')} - > - - - handleClick(props.activateTab, 'aggregations')} - > - - - - ); -} - -SearchProfilerTabs.propTypes = { - activeTab: PropTypes.any.isRequired, - activateTab: PropTypes.func.isRequired, - profileResponse: PropTypes.array.isRequired, -}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js b/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js deleted file mode 100644 index 6cfeb97f05ebd4..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/components/searchprofiler_tabs_directive.js +++ /dev/null @@ -1,21 +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 'ngreact'; - -import { wrapInI18nContext } from 'ui/i18n'; -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/searchprofiler', ['react']); - -import { SearchProfilerTabs } from './searchprofiler_tabs'; - -module.directive('searchProfilerTabs', function (reactDirective) { - return reactDirective( - wrapInI18nContext(SearchProfilerTabs), - undefined, - { restrict: 'E' } - ); -}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/_directives.scss b/x-pack/legacy/plugins/searchprofiler/public/directives/_directives.scss deleted file mode 100644 index 7f4f44748aa937..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/_directives.scss +++ /dev/null @@ -1,135 +0,0 @@ -.prfDevTool__panel { - border-bottom: $euiBorderThin; -} - -.prfDevTool__panelBody { - margin-top: $euiSizeS; - margin-left: $euiSizeL; -} - -// Profile details treeview -.prfDevTool__shardDetailsWrapper { - display: flex; - flex-direction: row-reverse; - justify-content: space-between; - align-items: center; -} - -.prfDevTool__shardDetails--dim small { - color: $euiColorDarkShade; -} - -.prfDevTool__shardBody { - margin-top: $euiSize; -} - -.prfDevTool__shardDetails { - line-height: 1; - overflow-wrap: break-word; - - &:disabled { - text-decoration: none !important; - cursor: default; - } -} - -.prfDevTool__shard { - border: none; -} - -.prfDevTool__index { - width: 100%; - padding: $euiSize $euiSizeS; -} - -.prfDevTool__tvRow--last { - cursor: pointer; -} - -.prfDevTool__tvRow, -.prfDevTool__tvHeader { - display: table; - width: 100%; - table-layout: fixed; -} - -.prfDevTool__tvHeader { - @include euiFontSizeXS; - color: $euiColorDarkShade; -} - -.prfDevTool__cell { - display: table-cell; - vertical-align: middle; - text-align: center; - padding: $euiSizeXS; - - &:first-of-type { - padding-left: 0; - } - - &:last-of-type { - padding-right: 0; - } -} - -.prfDevTool__detail { - font-size: $euiFontSizeS; - padding-left: $euiSizeL - 3px; // Alignment is weird - margin-bottom: $euiSizeS; - display: flex; - justify-content: space-between; - - .euiLink { - flex-shrink: 0; - } -} - -.prfDevTool__description { - text-align: left; -} - -.prfDevTool__time, -.prfDevTool__totalTime, -.prfDevTool__percentage { - width: $euiSize * 5.5; -} - -// BADGES (and those used for progress) - -.prfDevTool__badge { - border: none; - display: block; - // Force text to always be dark on top of white -> pink color - color: lightOrDarkTheme($euiColorDarkestShade, $euiColorLightestShade); -} - -.prfDevTool__progress--percent { - @include prfDevToolProgress; - width: $euiSize * 4; -} - -.prfDevTool__progress--time { - @include prfDevToolProgress(#FFAFAF); - background-color: #F5F5F5; // Must be light at all times - width: $euiSize * 15.5; - // Force text to always be dark on top of white -> pink color - color: lightOrDarkTheme($euiColorDarkestShade, $euiColorLightestShade); -} - -// Breakdown table -.prfDevTool__breakdown { - width:100%; -} - -.prfDevTool__flyoutSubtitle { - margin-bottom: $euiSizeS; - display: inline-block; -} - -@include euiBreakpoint('xs', 's') { - .prfDevTool__shardDetailsWrapper { - flex-direction: column; - align-items: flex-start; - } -} diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/_index.scss b/x-pack/legacy/plugins/searchprofiler/public/directives/_index.scss deleted file mode 100644 index 69a53dbafe2899..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'mixins'; -@import 'directives'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.html b/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.html deleted file mode 100644 index f58515390ded2d..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.html +++ /dev/null @@ -1,65 +0,0 @@ -
    -

    - {{detailRow.indexName}}
    - [{{detailRow.shardID}}][{{detailRow.shardNumber}}] -

    -
    - -
    -
    -
    -
    -
    -
    {{detailRow.query_type}}
    -
    -
    {{detailRow.lucene}}
    -
    - -
    -
    {{detailRow.time | msToPretty:3 }}
    -
    - -
    -
    {{detailRow.selfTime | msToPretty:3 }}
    -
    -
    -
    -

    -
    - - - - - - -
    {{breakdown.key}} - - {{breakdown.time | nsToPretty: 1}} - - - - - {{breakdown.relative}}% - -
    -
    -
    diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.js b/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.js deleted file mode 100644 index 717778ed3f4a4d..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/highlight_details/index.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import template from 'plugins/searchprofiler/directives/highlight_details/index.html'; -import { uiModules } from 'ui/modules'; - -const uiModule = uiModules.get('app/searchprofiler/directives', []); -uiModule.directive('highlightdetails', HighlightService => { - return { - restrict: 'E', - scope: { - data: '@' - }, - template: template, - link: $scope => { - - function render(data) { - if (!data) { - return; - } - data.breakdown = _.filter(data.breakdown, o => o.key.indexOf('_count') === -1); - $scope.detailRow = data; - } - - $scope.$watch(() => { - return HighlightService.details; - }, render); - - } - }; -}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/breakdown.js b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/breakdown.js deleted file mode 100644 index 1d88310dafbfd6..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/breakdown.js +++ /dev/null @@ -1,76 +0,0 @@ -export const breakdown = { - advance:0, - advance_count:0, - build_scorer:6273, - build_scorer_count:2, - create_weight:1852, - create_weight_count:1, - match:0, - match_count:0, - next_doc:2593093, - next_doc_count:27958, - score:2525502, - score_count:27948 -}; - -export const normalized = [ { key: 'next_doc', - time: 2593093, - relative: '50.6', - color: '#fad2d2', - tip: 'The time taken to advance the iterator to the next matching document.' }, - { key: 'score', - time: 2525502, - relative: '49.3', - color: '#fad2d2', - tip: 'The time taken in actually scoring the document against the query.' }, - { key: 'next_doc_count', - time: 27958, - relative: 0, - color: '#f5f5f5', - tip: '' }, - { key: 'score_count', - time: 27948, - relative: 0, - color: '#f5f5f5', - tip: '' }, - { key: 'build_scorer', - time: 6273, - relative: '0.1', - color: '#f5f5f5', - tip: 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.' }, - { key: 'create_weight', - time: 1852, - relative: '0.0', - color: '#f5f5f5', - tip: 'The time taken to create the Weight object, which holds temporary information during scoring.' }, - { key: 'build_scorer_count', - time: 2, - relative: 0, - color: '#f5f5f5', - tip: '' }, - { key: 'create_weight_count', - time: 1, - relative: 0, - color: '#f5f5f5', - tip: '' }, - { key: 'advance', - time: 0, - relative: '0.0', - color: '#f5f5f5', - tip: 'The time taken to advance the iterator to the next document.' }, - { key: 'advance_count', - time: 0, - relative: 0, - color: '#f5f5f5', - tip: '' }, - { key: 'match', - time: 0, - relative: '0.0', - color: '#f5f5f5', - tip: 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).' }, - { key: 'match_count', - time: 0, - relative: 0, - color: '#f5f5f5', - tip: '' }, - ]; diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/flatten_times.js b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/flatten_times.js deleted file mode 100644 index c2017f472bc0ba..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/flatten_times.js +++ /dev/null @@ -1,405 +0,0 @@ -/* eslint quotes: 0 */ -export const flatTimes = [ - { - id:"f1e689b1-dafe-4c2b-9a4d-9bd8f1a53803", - childrenIds:[ - "3339dca6-c34a-49f3-a534-27e46f238bcd", - "9b75ecdd-a1da-45eb-8d13-5bc5f472dba3", - "ddf5aa3e-4b22-4332-9d5e-79a6ae0cc9cb" - ], - lucene:"hour:1 hour:2 #MatchNoDocsQuery[\"User requested \"match_none\" query.\"]", - time:0.447365, - selfTime:0.057085, - timePercentage:"100.00", - query_type:"BooleanQuery", - absoluteColor:"#ffafaf", - depth:0, - hasChildren:true, - breakdown:[ - { - key:"create_weight", - time:401690, - relative:"89.8", - color:"#feb6b6", - tip:"The time taken to create the Weight object, which holds temporary information during scoring." - }, - { - key:"build_scorer", - time:45672, - relative:"10.2", - color:"#f6eeee", - tip:"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc." - }, - { - key:"build_scorer_count", - time:2, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"create_weight_count", - time:1, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next matching document." - }, - { - key:"match", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)." - }, - { - key:"match_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken in actually scoring the document against the query." - }, - { - key:"advance", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next document." - }, - { - key:"advance_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - } - ] - }, - { - id:"3339dca6-c34a-49f3-a534-27e46f238bcd", - parentId:"f1e689b1-dafe-4c2b-9a4d-9bd8f1a53803", - childrenIds:[ - - ], - lucene:"hour:1", - time:0.192502, - selfTime:0.192502, - timePercentage:"43.03", - query_type:"TermQuery", - absoluteColor:"#f9d7d7", - depth:1, - breakdown:[ - { - key:"create_weight", - time:190989, - relative:"99.2", - color:"#ffb0b0", - tip:"The time taken to create the Weight object, which holds temporary information during scoring." - }, - { - key:"build_scorer", - time:1510, - relative:"0.8", - color:"#f5f4f4", - tip:"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc." - }, - { - key:"build_scorer_count", - time:2, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"create_weight_count", - time:1, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next matching document." - }, - { - key:"match", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)." - }, - { - key:"match_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken in actually scoring the document against the query." - }, - { - key:"advance", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next document." - }, - { - key:"advance_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - } - ] - }, - { - id:"9b75ecdd-a1da-45eb-8d13-5bc5f472dba3", - parentId:"f1e689b1-dafe-4c2b-9a4d-9bd8f1a53803", - childrenIds:[ - - ], - lucene:"hour:2", - time:0.162608, - selfTime:0.162608, - timePercentage:"36.35", - query_type:"TermQuery", - absoluteColor:"#f9dcdc", - depth:1, - breakdown:[ - { - key:"create_weight", - time:162016, - relative:"99.6", - color:"#ffafaf", - tip:"The time taken to create the Weight object, which holds temporary information during scoring." - }, - { - key:"build_scorer", - time:589, - relative:"0.4", - color:"#f5f5f5", - tip:"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc." - }, - { - key:"build_scorer_count", - time:2, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"create_weight_count", - time:1, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next matching document." - }, - { - key:"match", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)." - }, - { - key:"match_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken in actually scoring the document against the query." - }, - { - key:"advance", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next document." - }, - { - key:"advance_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - } - ] - }, - { - id:"ddf5aa3e-4b22-4332-9d5e-79a6ae0cc9cb", - parentId:"f1e689b1-dafe-4c2b-9a4d-9bd8f1a53803", - childrenIds:[ - - ], - lucene:"MatchNoDocsQuery[\"User requested \"match_none\" query.\"]", - time:0.03517, - selfTime:0.03517, - timePercentage:"7.86", - query_type:"MatchNoDocsQuery", - absoluteColor:"#f6efef", - depth:1, - breakdown:[ - { - key:"build_scorer", - time:32522, - relative:"92.5", - color:"#feb4b4", - tip:"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc." - }, - { - key:"create_weight", - time:2645, - relative:"7.5", - color:"#f6f0f0", - tip:"The time taken to create the Weight object, which holds temporary information during scoring." - }, - { - key:"build_scorer_count", - time:2, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"create_weight_count", - time:1, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next matching document." - }, - { - key:"match", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)." - }, - { - key:"match_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"next_doc_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - }, - { - key:"score", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken in actually scoring the document against the query." - }, - { - key:"advance", - time:0, - relative:"0.0", - color:"#f5f5f5", - tip:"The time taken to advance the iterator to the next document." - }, - { - key:"advance_count", - time:0, - relative:0, - color:"#f5f5f5", - tip:"" - } - ] - } -]; diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/normalize_indices.js b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/normalize_indices.js deleted file mode 100644 index fee12c17e7d580..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/normalize_indices.js +++ /dev/null @@ -1,4 +0,0 @@ -/*eslint-disable */ -export const inputIndices = JSON.parse('{"test":{"shards":[{"id":["F-R7QxH4S42fMnPfmFUKMQ","test","0"],"searches":[{"query":null,"rewrite_time":2656,"collector":[{"name":"MultiCollector","reason":"search_multi","time":"0.1815780000ms","children":[{"name":"SimpleTopScoreDocCollector","reason":"search_top_hits","time":"0.02393700000ms"},{"name":"ProfilingAggregator: [org.elasticsearch.search.profile.aggregation.ProfilingAggregator@43c8a536]","reason":"aggregation","time":"0.1140000000ms"}]}],"flat":[{"id":"af697413-f76b-458e-b265-f4930dbdee2a","childrenIds":[],"lucene":"name:george","time":0.219343,"selfTime":0.219343,"timePercentage":"100.00","query_type":"TermQuery","absoluteColor":"#ffafaf","depth":0,"breakdown":[{"key":"create_weight","time":160673,"relative":"73.3","color":"#fcc2c2","tip":"The time taken to create the Weight object, which holds temporary information during scoring."},{"key":"build_scorer","time":50157,"relative":"22.9","color":"#f7e5e5","tip":"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc."},{"key":"score","time":5783,"relative":"2.6","color":"#f5f3f3","tip":"The time taken in actually scoring the document against the query."},{"key":"next_doc","time":2718,"relative":"1.2","color":"#f5f4f4","tip":"The time taken to advance the iterator to the next matching document."},{"key":"build_scorer_count","time":5,"relative":0,"color":"#f5f5f5","tip":""},{"key":"next_doc_count","time":4,"relative":0,"color":"#f5f5f5","tip":""},{"key":"score_count","time":2,"relative":0,"color":"#f5f5f5","tip":""},{"key":"create_weight_count","time":1,"relative":0,"color":"#f5f5f5","tip":""},{"key":"match","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)."},{"key":"match_count","time":0,"relative":0,"color":"#f5f5f5","tip":""},{"key":"advance","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to advance the iterator to the next document."},{"key":"advance_count","time":0,"relative":0,"color":"#f5f5f5","tip":""}]}]}],"aggregations":[{"type":"org.elasticsearch.search.aggregations.metrics.stats.StatsAggregator","description":"stats","time":"0.03053500000ms","breakdown":{"reduce":0,"build_aggregation":9447,"build_aggregation_count":1,"initialize":5589,"initialize_count":1,"reduce_count":0,"collect":15495,"collect_count":2}}],"time":{"searches":0.219343,"aggregations":0},"color":{"searches":0,"aggregations":0},"relative":{"searches":0,"aggregations":0},"rewrite_time":2656}],"time":{"searches":0.219343,"aggregations":0},"name":"test"}}'); - -export const normalizedIndices = JSON.parse('[{"shards":[{"id":["F-R7QxH4S42fMnPfmFUKMQ","test","0"],"searches":[{"query":null,"rewrite_time":2656,"collector":[{"name":"MultiCollector","reason":"search_multi","time":"0.1815780000ms","children":[{"name":"SimpleTopScoreDocCollector","reason":"search_top_hits","time":"0.02393700000ms"},{"name":"ProfilingAggregator: [org.elasticsearch.search.profile.aggregation.ProfilingAggregator@43c8a536]","reason":"aggregation","time":"0.1140000000ms"}]}],"flat":[{"id":"af697413-f76b-458e-b265-f4930dbdee2a","childrenIds":[],"lucene":"name:george","time":0.219343,"selfTime":0.219343,"timePercentage":"100.00","query_type":"TermQuery","absoluteColor":"#ffafaf","depth":0,"breakdown":[{"key":"create_weight","time":160673,"relative":"73.3","color":"#fcc2c2","tip":"The time taken to create the Weight object, which holds temporary information during scoring."},{"key":"build_scorer","time":50157,"relative":"22.9","color":"#f7e5e5","tip":"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc."},{"key":"score","time":5783,"relative":"2.6","color":"#f5f3f3","tip":"The time taken in actually scoring the document against the query."},{"key":"next_doc","time":2718,"relative":"1.2","color":"#f5f4f4","tip":"The time taken to advance the iterator to the next matching document."},{"key":"build_scorer_count","time":5,"relative":0,"color":"#f5f5f5","tip":""},{"key":"next_doc_count","time":4,"relative":0,"color":"#f5f5f5","tip":""},{"key":"score_count","time":2,"relative":0,"color":"#f5f5f5","tip":""},{"key":"create_weight_count","time":1,"relative":0,"color":"#f5f5f5","tip":""},{"key":"match","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)."},{"key":"match_count","time":0,"relative":0,"color":"#f5f5f5","tip":""},{"key":"advance","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to advance the iterator to the next document."},{"key":"advance_count","time":0,"relative":0,"color":"#f5f5f5","tip":""}]}]}],"aggregations":[{"type":"org.elasticsearch.search.aggregations.metrics.stats.StatsAggregator","description":"stats","time":"0.03053500000ms","breakdown":{"reduce":0,"build_aggregation":9447,"build_aggregation_count":1,"initialize":5589,"initialize_count":1,"reduce_count":0,"collect":15495,"collect_count":2}}],"time":{"searches":0.219343,"aggregations":0},"color":{"searches":"#ffafaf","aggregations":0},"relative":{"searches":"100.00","aggregations":0},"rewrite_time":2656}],"time":{"searches":0.219343,"aggregations":0},"name":"test"}]'); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.html b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.html deleted file mode 100644 index e6994a07aa6d16..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.html +++ /dev/null @@ -1,115 +0,0 @@ -
    -
    -
    - - {{:: 'xpack.searchProfiler.profileTree.cumulativeTimeTitle' | i18n: { defaultMessage: "Cumulative Time:" } }} {{ index.time[target] | msToPretty: 3 }} - -
    -
    -

    - - {{index.name}} -

    -
    -
    - -
    - -
    -
    -
    -
    - - - {{shard.time[target] | msToPretty: 3}} - -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    - - {{row.selfTime | msToPretty: 1}} - -
    -
    - - {{row.time | msToPretty: 1}} - -
    -
    - - - {{row.timePercentage}}% - -
    -
    - -
    - - - {{row.lucene | limitTo : 120}}{{row.lucene.length > 120 ? '...' : ''}} - - - -
    -
    -
    -
    -
    -
    -
    diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.js b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.js deleted file mode 100644 index b212439e98394a..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/index.js +++ /dev/null @@ -1,172 +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 template from 'plugins/searchprofiler/directives/profile_tree/index.html'; -import { - closeNode, - normalizeIndices, - calcTimes, - normalizeTimes, - flattenResults -} from 'plugins/searchprofiler/directives/profile_tree/util'; -import { uiModules } from 'ui/modules'; - -const uiModule = uiModules.get('app/searchprofiler/directives', []); -uiModule.directive('profiletree', HighlightService => { - return { - restrict: 'E', - scope: { - data: '=', - target: '@' - }, - template: template, - link: $scope => { - $scope.visible = { - 'foo': {} - }; - $scope.indexVisibility = {}; - $scope.highlightedRow = null; - - $scope.updateDetail = (row, indexName, shardID, shardNumber) => { - HighlightService.details = row; - HighlightService.details.indexName = indexName; - HighlightService.details.shardID = shardID; - HighlightService.details.shardNumber = shardNumber; - HighlightService.details.highlightedRow = row.id; - }; - - $scope.getHighlightedRow = () => { - if (HighlightService.details) { - return HighlightService.details.highlightedRow; - } - return null; - }; - - $scope.toggle = id => { - // If the branch is open and toggled close, we need to - // also close the children - if ($scope.visible[id].visible === true) { - closeNode($scope.visible, id); - } else { - // Otherwise just toggle on - $scope.visible[id].visible = true; - } - }; - - function render(data) { - if (data.length === 0) { - return; - } - - $scope.visible = {}; - let indices = {}; - - for (const shard of data) { - initShardTargets(shard); - - if ($scope.target === 'searches') { - shard.time[$scope.target] = collectSearchTimes(shard); - } else if ($scope.target === 'aggregations') { - shard.time[$scope.target] = collectAggTimes(shard); - } - if (!indices[shard.id[1]]) { - indices[shard.id[1]] = { - shards: [], - time: { - searches: 0, - aggregations: 0 - }, - name: shard.id[1] - }; - } - indices[shard.id[1]].shards.push(shard); - indices[shard.id[1]].time[$scope.target] += shard.time[$scope.target]; - } - data = null; - const finalIndices = normalizeIndices(indices, $scope.indexVisibility, $scope.target); - indices = null; - - $scope.profileResponse = finalIndices; - } - - function collectSearchTimes(shard) { - if (shard.searches == null) { - return 0; - } - shard.rewrite_time = 0; - - let shardTime = 0; - for (const search of shard.searches) { - shard.rewrite_time += search.rewrite_time; - const totalTime = calcTimes(search.query); - shardTime += totalTime; - normalizeTimes(search.query, totalTime, 0); - - const flat = []; - flattenResults(search.query, flat, 0, $scope.visible); - search.flat = flat; - search.query = null; - } - return shardTime; - } - - function collectAggTimes(shard) { - if (shard.aggregations == null) { - return 0; - } - let shardTime = 0; - for (const agg of shard.aggregations) { - const totalTime = calcTimes([agg]); - shardTime += totalTime; - } - for (const agg of shard.aggregations) { - normalizeTimes([agg], shardTime, 0); - - const flat = []; - flattenResults([agg], flat, 0, $scope.visible); - agg.flat = flat; - } - return shardTime; - } - - // TODO the addition of aggregation profiling made the mutability of - // `shards` a liability. Previously we set things directly on the shards - // tree because it was the only source of data. Now we have agg data, - // so final, accumulated stats need to be saved on a per-target basis - // - // In the future, we should really remove this setup and create two immutable - // result sets that are generated from a single (also immutable) input set of - // `shards` data - // - // Particularly important if/when we add a third target - function initShardTargets(shard) { - if (!shard.time) { - shard.time = { - searches: 0, - aggregations: 0 - }; - } - - if (!shard.color) { - shard.color = { - searches: 0, - aggregations: 0 - }; - } - - if (!shard.relative) { - shard.relative = { - searches: 0, - aggregations: 0 - }; - } - } - - $scope.$watch('data', render); - - } - }; -}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/util.js b/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/util.js deleted file mode 100644 index 6190d299dcf10d..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/util.js +++ /dev/null @@ -1,219 +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 uuid from 'uuid'; -import tinycolor from 'tinycolor2'; -import _ from 'lodash'; - -const comparator = (v1, v2) => { - if (v1 < v2) { - return 1; - } - return (v1 > v2) ? -1 : 0; -}; - -function getToolTip(key) { - switch (key) { - case 'build_scorer': - return 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.'; - case 'create_weight': - return 'The time taken to create the Weight object, which holds temporary information during scoring.'; - case 'next_doc': - return 'The time taken to advance the iterator to the next matching document.'; - case 'score': - return 'The time taken in actually scoring the document against the query.'; - case 'match': - return 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).'; - case 'advance': - return 'The time taken to advance the iterator to the next document.'; - default: - return ''; - } -} - -export function timeInMilliseconds(data) { - if (data.time_in_nanos) { - return data.time_in_nanos / 1000000; - } - - if (typeof data.time === 'string') { - return data.time.replace('ms', ''); - } - - return data.time; -} - -export function calcTimes(data, parentId) { - if (data == null) { - return; - } - - let totalTime = 0; - //First pass to collect total - for (const child of data) { - totalTime += timeInMilliseconds(child); - - child.id = uuid.v4(); - child.parentId = parentId; - child.childrenIds = []; - child.breakdown = normalizeBreakdown(child.breakdown); - - let childrenTime = 0; - if (child.children != null && child.children.length !== 0) { - childrenTime = calcTimes(child.children, child.id); - child.hasChildren = true; - - // Save the IDs of our children, has to be called after calcTimes recursion above - for (const c of child.children) { - child.childrenIds.push(c.id); - } - } - child.selfTime = (timeInMilliseconds(child) - childrenTime); - } - return totalTime; -} - -export function normalizeBreakdown(breakdown) { - const final = []; - const total = Object.keys(breakdown).reduce((partialTotal, currentKey) => { - if (currentKey.indexOf('_count') === -1) { - partialTotal += breakdown[currentKey]; - } - return partialTotal; - }, 0); - Object.keys(breakdown).sort().forEach(key => { - let relative = 0; - if (key.indexOf('_count') === -1) { - relative = ((breakdown[key] / total) * 100).toFixed(1); - } - final.push({ - key: key, - time: breakdown[key], - relative: relative, - color: tinycolor.mix('#F5F5F5', '#FFAFAF', relative).toHexString(), - tip: getToolTip(key) - }); - }); - - // Sort by time descending and then key ascending - return final.sort((a, b) => { - if (comparator(a.time, b.time) !== 0) { - return comparator(a.time, b.time); - } - - return -1 * comparator(a.key, b.key); - }); -} - -export function normalizeTimes(data, totalTime, depth) { - //Second pass to normalize - for (const child of data) { - child.timePercentage = ((timeInMilliseconds(child) / totalTime) * 100).toFixed(2); - child.absoluteColor = tinycolor.mix('#F5F5F5', '#FFAFAF', child.timePercentage).toHexString(); - child.depth = depth; - - if (child.children != null && child.children.length !== 0) { - normalizeTimes(child.children, totalTime, depth + 1); - } - } - - data.sort((a, b) => comparator(timeInMilliseconds(a), timeInMilliseconds(b))); -} - -export function normalizeIndices(indices, visibility, target) { - // Sort the shards per-index - let sortQueryComponents; - if (target === 'searches') { - sortQueryComponents = (a, b) => { - const aTime = _.sum(a.searches, (search) => { - return search.flat[0].time; - }); - const bTime = _.sum(b.searches, (search) => { - return search.flat[0].time; - }); - - return comparator(aTime, bTime); - }; - } else if (target === 'aggregations') { - sortQueryComponents = (a, b) => { - const aTime = _.sum(a.aggregations, (agg) => { - return agg.flat[0].time; - }); - const bTime = _.sum(b.aggregations, (agg) => { - return agg.flat[0].time; - }); - - return comparator(aTime, bTime); - }; - } - const sortedIndices = []; - for (const [key, index] of Object.entries(indices)) { - index.shards.sort(sortQueryComponents); - for (const shard of index.shards) { - shard.relative[target] = ((shard.time[target] / index.time[target]) * 100).toFixed(2); - shard.color[target] = tinycolor.mix('#F5F5F5', '#FFAFAF', shard.relative[target]).toHexString(); - } - sortedIndices.push(index); - visibility[key] = false; - } - - // And now sort the indices themselves - sortedIndices.sort((a, b) => comparator(a.time, b.time)); - return sortedIndices; -} - -export function flattenResults(data, accumulator, depth, visibleMap) { - if (data == null) { - return; - } - - for (const child of data) { - - // For bwc of older profile responses - if (!child.description) { - child.description = child.lucene; - child.lucene = null; - - child.type = child.query_type; - child.query_type = null; - } - accumulator.push({ - id: child.id, - parentId: child.parentId, - childrenIds: child.childrenIds, - lucene: child.description, - time: timeInMilliseconds(child), - selfTime: child.selfTime, - timePercentage: child.timePercentage, - query_type: child.type.split('.').pop(), - absoluteColor: child.absoluteColor, - depth: depth, - hasChildren: child.hasChildren, - breakdown: child.breakdown - }); - - visibleMap[child.id] = { - visible: child.timePercentage > 20, - children: child.children - }; - - if (child.children != null && child.children.length !== 0) { - flattenResults(child.children, accumulator, depth + 1, visibleMap); - } - } -} - -export function closeNode(visibleMap, id) { - visibleMap[id].visible = false; - - if (visibleMap[id].children == null || visibleMap[id].children.length === 0) { - return; - } - - for (const child of visibleMap[id].children) { - closeNode(visibleMap, child.id); - } -} diff --git a/x-pack/legacy/plugins/searchprofiler/public/legacy.ts b/x-pack/legacy/plugins/searchprofiler/public/legacy.ts new file mode 100644 index 00000000000000..61dabe8ac7b05c --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/legacy.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 @kbn/eslint/no-restricted-paths */ +import { npSetup } from 'ui/new_platform'; +import { I18nContext } from 'ui/i18n'; +import uiRoutes from 'ui/routes'; +import 'ui/capabilities/route_setup'; +// @ts-ignore +import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; +// @ts-ignore +import { formatAngularHttpError } from 'ui/notify/lib'; +import 'ui/autoload/all'; +/* eslint-enable @kbn/eslint/no-restricted-paths */ + +import { ApplicationSetup } from 'src/core/public'; +import { plugin } from './np_ready'; + +const pluginInstance = plugin({} as any); + +const template = ` +
    +`; + +uiRoutes.when('/dev_tools/searchprofiler', { + template, + requireUICapability: 'dev_tools.show', + controller: $scope => { + $scope.startReactApp = () => { + const el = document.querySelector('#searchProfilerAppRoot'); + if (!el) { + const errorMessage = 'Could not mount Searchprofiler App!'; + npSetup.core.fatalErrors.add(errorMessage); + throw new Error(errorMessage); + } + + const coreApplicationSetupShim: ApplicationSetup = { + register(app: any) { + const unmount = app.mount(); + $scope.$on('$destroy', () => unmount()); + }, + registerMountContext: {} as any, + }; + + pluginInstance.setup( + { + ...npSetup.core, + application: coreApplicationSetupShim, + }, + { + __LEGACY: { + I18nContext, + licenseEnabled: xpackInfo.get('features.searchprofiler.enableAppLink'), + notifications: npSetup.core.notifications.toasts, + formatAngularHttpError, + el, + }, + } + ); + }; + }, +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/boot.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/boot.tsx new file mode 100644 index 00000000000000..fa02124f8a2454 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/boot.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { render, unmountComponentAtNode } from 'react-dom'; +import React from 'react'; +import { HttpStart as Http, ToastsSetup } from 'src/core/public'; +import { App } from '.'; + +export interface Dependencies { + el: HTMLElement; + http: Http; + licenseEnabled: boolean; + I18nContext: any; + notifications: ToastsSetup; + formatAngularHttpError: any; +} + +export type AppDependencies = Omit; + +export function boot(deps: Dependencies): () => void { + const { el, ...rest } = deps; + render(, deps.el); + return () => unmountComponentAtNode(deps.el); +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.test.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.test.tsx new file mode 100644 index 00000000000000..cf8052cbf35806 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.test.tsx @@ -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. + */ + +import { registerTestBed } from '../../../../../../../../test_utils'; +import { HighlightDetailsFlyout, Props } from '.'; + +describe('Highlight Details Flyout', () => { + it('renders', async () => { + const props: Props = { + onClose: () => {}, + shard: { + aggregations: [], + id: ['test', 'test', 'test'], + searches: [], + color: '#fff', + time: 123, + relative: 100, + }, + operation: { + parent: null, + breakdown: [ + { + color: 'test', + key: 'test', + relative: 100, + tip: 'test', + time: 100, + }, + { + color: 'test', + key: 'test', + relative: 100, + tip: 'test', + time: 100, + }, + { + color: 'test', + key: 'test', + relative: 100, + tip: 'test', + time: 100, + }, + ], + lucene: 'test', + query_type: 'test', + selfTime: 100, + time: 100, + children: [], + timePercentage: 100, + hasChildren: false, + visible: true, + absoluteColor: '123', + }, + indexName: 'test', + }; + + const init = registerTestBed(HighlightDetailsFlyout); + await init(props); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.tsx new file mode 100644 index 00000000000000..6ba39d15b83411 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_flyout.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiIconTip, + EuiText, + EuiCodeBlock, +} from '@elastic/eui'; + +import { msToPretty } from '../../utils'; +import { HighlightDetailsTable } from './highlight_details_table'; +import { Operation, Shard } from '../../types'; + +export interface Props { + operation: Operation; + shard: Shard; + indexName: string; + onClose: () => void; +} + +const FlyoutEntry = ({ + title, + body, +}: { + title: string | JSX.Element; + body: string | JSX.Element; +}) => ( + <> +
    {title}
    +
    {body}
    + +); + +export const HighlightDetailsFlyout = ({ indexName, operation, shard, onClose }: Props) => { + return ( + onClose()}> + + {indexName} + + [{/* shard id */ shard.id[0]}][{/* shard number */ shard.id[2]}] + + + + +
    + {/* Type Entry */} + + {/* Description Entry */} + {operation.lucene!}} + /> + {/* Total Time Entry */} + + {i18n.translate('xpack.searchProfiler.highlightDetails.totalTimeTitle', { + defaultMessage: 'Total time', + })}{' '} + + + } + body={msToPretty(operation.time, 3)} + /> + {/* Self Time Entry */} + + {i18n.translate('xpack.searchProfiler.highlightDetails.selfTimeTitle', { + defaultMessage: 'Self time', + })}{' '} + + + } + body={msToPretty(operation.selfTime || 0, 3)} + /> + {/* Breakdown Table Entry */} + } + /> +
    +
    +
    +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_table.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_table.tsx new file mode 100644 index 00000000000000..4bfa7365de1efa --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/highlight_details_table.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiBasicTable, EuiToolTip, EuiBadge } from '@elastic/eui'; + +import { BreakdownItem } from '../../types'; +import { nsToPretty } from '../../utils'; +import { PercentageBadge } from '../percentage_badge'; + +interface Props { + breakdown: BreakdownItem[]; +} + +export const HighlightDetailsTable = ({ breakdown }: Props) => { + const columns = [ + { + name: 'Description', + render: (item: BreakdownItem) => ( + + {item.key} + + ), + }, + { + name: 'Time', + render: (item: BreakdownItem) => ( + + {nsToPretty(item.time, 1)} + + ), + }, + { + name: 'Percentage', + render: (item: BreakdownItem) => ( + + ), + }, + ]; + + return ; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/index.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/index.ts similarity index 67% rename from x-pack/legacy/plugins/searchprofiler/public/directives/index.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/index.ts index 4bd4146a1a0fe3..36ae010fabfa48 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/index.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/highlight_details_flyout/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import 'plugins/searchprofiler/directives/profile_tree'; -import 'plugins/searchprofiler/directives/highlight_details'; +export { HighlightDetailsFlyout, Props } from './highlight_details_flyout'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/index.ts new file mode 100644 index 00000000000000..db1fea0033db5c --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/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 { SearchProfilerTabs } from './searchprofiler_tabs'; +export { LicenseWarningNotice } from './license_warning_notice'; +export { ProfileTree, OnHighlightChangeArgs } from './profile_tree'; +export { HighlightDetailsFlyout } from './highlight_details_flyout'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.test.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.test.ts new file mode 100644 index 00000000000000..ebe7a00737868f --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.test.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. + */ + +import { registerTestBed } from '../../../../../../../test_utils'; + +import { LicenseWarningNotice } from './license_warning_notice'; + +describe('License Error Notice', () => { + it('renders', async () => { + const init = registerTestBed(LicenseWarningNotice); + await init({}); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.tsx new file mode 100644 index 00000000000000..da9991529e7d44 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/license_warning_notice.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiCallOut, EuiText, EuiLink, EuiCode } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const LicenseWarningNotice = () => { + const registerLicenseLinkLabel = i18n.translate('xpack.searchProfiler.registerLicenseLinkLabel', { + defaultMessage: 'register a license', + }); + + const trialLicense = i18n.translate('xpack.searchProfiler.trialLicenseTitle', { + defaultMessage: 'Trial', + }); + + const basicLicense = i18n.translate('xpack.searchProfiler.basicLicenseTitle', { + defaultMessage: 'Basic', + }); + + const goldLicense = i18n.translate('xpack.searchProfiler.goldLicenseTitle', { + defaultMessage: 'Gold', + }); + + const platinumLicense = i18n.translate('xpack.searchProfiler.platinumLicenseTitle', { + defaultMessage: 'Platinum', + }); + + return ( +
    + + +

    + + {trialLicense}, {basicLicense},{' '} + {goldLicense} + + ), + platinumLicenseType: {platinumLicense}, + }} + /> +

    +

    + + {registerLicenseLinkLabel} + + ), + }} + /> +

    +
    +
    +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/percentage_badge.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/percentage_badge.tsx new file mode 100644 index 00000000000000..4b53b2e3c18c56 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/percentage_badge.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiBadge } from '@elastic/eui'; +import classNames from 'classnames'; + +interface Props { + timePercentage: number; + label: string; + valueType?: 'percent' | 'time'; +} + +/** + * This component has IE specific provision for rendering the percentage portion of the badge correctly. + * + * This component uses CSS vars injected against the DOM element and resolves this in CSS to calculate + * how far the percent bar should be drawn. + */ +export const PercentageBadge = ({ timePercentage, label, valueType = 'percent' }: Props) => { + return ( + + + {label} + + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/breakdown.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/breakdown.ts new file mode 100644 index 00000000000000..a65af9a7a3ff3e --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/breakdown.ts @@ -0,0 +1,68 @@ +export const breakdown = { + advance: 0, + advance_count: 0, + build_scorer: 6273, + build_scorer_count: 2, + create_weight: 1852, + create_weight_count: 1, + match: 0, + match_count: 0, + next_doc: 2593093, + next_doc_count: 27958, + score: 2525502, + score_count: 27948, +}; + +export const normalized = [ + { + key: 'next_doc', + time: 2593093, + relative: '50.6', + color: '#fad2d2', + tip: 'The time taken to advance the iterator to the next matching document.', + }, + { + key: 'score', + time: 2525502, + relative: '49.3', + color: '#fad2d2', + tip: 'The time taken in actually scoring the document against the query.', + }, + { key: 'next_doc_count', time: 27958, relative: 0, color: '#f5f5f5', tip: '' }, + { key: 'score_count', time: 27948, relative: 0, color: '#f5f5f5', tip: '' }, + { + key: 'build_scorer', + time: 6273, + relative: '0.1', + color: '#f5f5f5', + tip: + 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.', + }, + { + key: 'create_weight', + time: 1852, + relative: '0.0', + color: '#f5f5f5', + tip: + 'The time taken to create the Weight object, which holds temporary information during scoring.', + }, + { key: 'build_scorer_count', time: 2, relative: 0, color: '#f5f5f5', tip: '' }, + { key: 'create_weight_count', time: 1, relative: 0, color: '#f5f5f5', tip: '' }, + { + key: 'advance', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: 'The time taken to advance the iterator to the next document.', + }, + { key: 'advance_count', time: 0, relative: 0, color: '#f5f5f5', tip: '' }, + { + key: 'match', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: + 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).', + }, + { key: 'match_count', time: 0, relative: 0, color: '#f5f5f5', tip: '' }, +]; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/normalize_indices.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/normalize_indices.ts new file mode 100644 index 00000000000000..bd8e44d82379ba --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/normalize_indices.ts @@ -0,0 +1,8 @@ +/*eslint-disable */ +export const inputIndices = JSON.parse( + '{"test":{"shards":[{"id":["F-R7QxH4S42fMnPfmFUKMQ","test","0"],"searches":[{"query":null,"rewrite_time":2656,"collector":[{"name":"MultiCollector","reason":"search_multi","time":"0.1815780000ms","children":[{"name":"SimpleTopScoreDocCollector","reason":"search_top_hits","time":"0.02393700000ms"},{"name":"ProfilingAggregator: [org.elasticsearch.search.profile.aggregation.ProfilingAggregator@43c8a536]","reason":"aggregation","time":"0.1140000000ms"}]}],"flat":[{"id":"af697413-f76b-458e-b265-f4930dbdee2a","childrenIds":[],"lucene":"name:george","time":0.219343,"selfTime":0.219343,"timePercentage":"100.00","query_type":"TermQuery","absoluteColor":"#ffafaf","depth":0,"breakdown":[{"key":"create_weight","time":160673,"relative":"73.3","color":"#fcc2c2","tip":"The time taken to create the Weight object, which holds temporary information during scoring."},{"key":"build_scorer","time":50157,"relative":"22.9","color":"#f7e5e5","tip":"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc."},{"key":"score","time":5783,"relative":"2.6","color":"#f5f3f3","tip":"The time taken in actually scoring the document against the query."},{"key":"next_doc","time":2718,"relative":"1.2","color":"#f5f4f4","tip":"The time taken to advance the iterator to the next matching document."},{"key":"build_scorer_count","time":5,"relative":0,"color":"#f5f5f5","tip":""},{"key":"next_doc_count","time":4,"relative":0,"color":"#f5f5f5","tip":""},{"key":"score_count","time":2,"relative":0,"color":"#f5f5f5","tip":""},{"key":"create_weight_count","time":1,"relative":0,"color":"#f5f5f5","tip":""},{"key":"match","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)."},{"key":"match_count","time":0,"relative":0,"color":"#f5f5f5","tip":""},{"key":"advance","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to advance the iterator to the next document."},{"key":"advance_count","time":0,"relative":0,"color":"#f5f5f5","tip":""}]}]}],"aggregations":[{"type":"org.elasticsearch.search.aggregations.metrics.stats.StatsAggregator","description":"stats","time":"0.03053500000ms","breakdown":{"reduce":0,"build_aggregation":9447,"build_aggregation_count":1,"initialize":5589,"initialize_count":1,"reduce_count":0,"collect":15495,"collect_count":2}}],"time":0.219343,"color":0,"relative":0,"rewrite_time":2656}],"time":0.219343,"name":"test"}}' +); + +export const normalizedIndices = JSON.parse( + '[{"shards":[{"id":["F-R7QxH4S42fMnPfmFUKMQ","test","0"],"searches":[{"query":null,"rewrite_time":2656,"collector":[{"name":"MultiCollector","reason":"search_multi","time":"0.1815780000ms","children":[{"name":"SimpleTopScoreDocCollector","reason":"search_top_hits","time":"0.02393700000ms"},{"name":"ProfilingAggregator: [org.elasticsearch.search.profile.aggregation.ProfilingAggregator@43c8a536]","reason":"aggregation","time":"0.1140000000ms"}]}],"flat":[{"id":"af697413-f76b-458e-b265-f4930dbdee2a","childrenIds":[],"lucene":"name:george","time":0.219343,"selfTime":0.219343,"timePercentage":"100.00","query_type":"TermQuery","absoluteColor":"#ffafaf","depth":0,"breakdown":[{"key":"create_weight","time":160673,"relative":"73.3","color":"#fcc2c2","tip":"The time taken to create the Weight object, which holds temporary information during scoring."},{"key":"build_scorer","time":50157,"relative":"22.9","color":"#f7e5e5","tip":"The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc."},{"key":"score","time":5783,"relative":"2.6","color":"#f5f3f3","tip":"The time taken in actually scoring the document against the query."},{"key":"next_doc","time":2718,"relative":"1.2","color":"#f5f4f4","tip":"The time taken to advance the iterator to the next matching document."},{"key":"build_scorer_count","time":5,"relative":0,"color":"#f5f5f5","tip":""},{"key":"next_doc_count","time":4,"relative":0,"color":"#f5f5f5","tip":""},{"key":"score_count","time":2,"relative":0,"color":"#f5f5f5","tip":""},{"key":"create_weight_count","time":1,"relative":0,"color":"#f5f5f5","tip":""},{"key":"match","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to execute a secondary, more precise scoring phase (used by phrase queries)."},{"key":"match_count","time":0,"relative":0,"color":"#f5f5f5","tip":""},{"key":"advance","time":0,"relative":"0.0","color":"#f5f5f5","tip":"The time taken to advance the iterator to the next document."},{"key":"advance_count","time":0,"relative":0,"color":"#f5f5f5","tip":""}]}]}],"aggregations":[{"type":"org.elasticsearch.search.aggregations.metrics.stats.StatsAggregator","description":"stats","time":"0.03053500000ms","breakdown":{"reduce":0,"build_aggregation":9447,"build_aggregation_count":1,"initialize":5589,"initialize_count":1,"reduce_count":0,"collect":15495,"collect_count":2}}],"time":0.219343,"color":"#ffafaf","relative":"100.00","rewrite_time":2656}],"time":0.219343,"name":"test"}]' +); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/normalize_times.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/normalize_times.ts similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/fixtures/normalize_times.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/normalize_times.ts diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/processed_search_response.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/processed_search_response.ts new file mode 100644 index 00000000000000..ede36ba2b01699 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/processed_search_response.ts @@ -0,0 +1,344 @@ +/* + * 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 { produce } from 'immer'; + +const shard1 = { + id: ['L22w_FX2SbqlQYOP5QrYDg', '.kibana_1', '0'], + searches: [], + aggregations: [], + time: 0.058419, + color: '#ffafaf', + relative: '100.00', + rewrite_time: 14670, +}; + +const searchRoot = { + query: null, + rewrite_time: 14670, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 14706, + children: [ + { + name: 'SimpleTopScoreDocCollector', + reason: 'search_top_hits', + time_in_nanos: 7851, + }, + ], + }, + ], + treeRoot: null, +}; + +const search1 = { + type: 'ConstantScoreQuery', + description: 'ConstantScore(DocValuesFieldExistsQuery [field=_primary_term])', + time_in_nanos: 58419, + breakdown: [ + { + key: 'build_scorer', + time: 40061, + relative: '68.7', + color: '#fcc5c5', + tip: + 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.', + }, + { + key: 'create_weight', + time: 8238, + relative: '14.1', + color: '#f6ebeb', + tip: + 'The time taken to create the Weight object, which holds temporary information during scoring.', + }, + { + key: 'next_doc', + time: 5767, + relative: '9.9', + color: '#f6eeee', + tip: 'The time taken to advance the iterator to the next matching document.', + }, + { + key: 'advance', + time: 2849, + relative: '4.9', + color: '#f5f2f2', + tip: 'The time taken to advance the iterator to the next document.', + }, + { + key: 'score', + time: 1431, + relative: '2.5', + color: '#f5f3f3', + tip: 'The time taken in actually scoring the document against the query.', + }, + { + key: 'next_doc_count', + time: 24, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'score_count', + time: 24, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'build_scorer_count', + time: 16, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'advance_count', + time: 8, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'create_weight_count', + time: 1, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'compute_max_score', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'compute_max_score_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'match', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: + 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).', + }, + { + key: 'match_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'set_min_competitive_score', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'set_min_competitive_score_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'shallow_advance', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'shallow_advance_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + ], + children: [ + /* See search1Child */ + ], + hasChildren: true, + selfTime: 0.028784999999999998, + timePercentage: '100.00', + absoluteColor: '#ffafaf', + depth: 0, + parent: null, + time: 0.058419, + lucene: 'ConstantScore(DocValuesFieldExistsQuery [field=_primary_term])', + query_type: 'ConstantScoreQuery', + visible: true, +}; + +const search1Child = { + type: 'DocValuesFieldExistsQuery', + description: 'DocValuesFieldExistsQuery [field=_primary_term]', + time_in_nanos: 29634, + breakdown: [ + { + key: 'build_scorer', + time: 24059, + relative: '81.3', + color: '#fdbcbc', + tip: + 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.', + }, + { + key: 'next_doc', + time: 2434, + relative: '8.2', + color: '#f6efef', + tip: 'The time taken to advance the iterator to the next matching document.', + }, + { + key: 'create_weight', + time: 1586, + relative: '5.4', + color: '#f6f1f1', + tip: + 'The time taken to create the Weight object, which holds temporary information during scoring.', + }, + { + key: 'advance', + time: 1506, + relative: '5.1', + color: '#f6f1f1', + tip: 'The time taken to advance the iterator to the next document.', + }, + { + key: 'next_doc_count', + time: 24, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'build_scorer_count', + time: 16, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'advance_count', + time: 8, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'create_weight_count', + time: 1, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'compute_max_score', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'compute_max_score_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'match', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: + 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).', + }, + { + key: 'match_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'score', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: 'The time taken in actually scoring the document against the query.', + }, + { + key: 'score_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'set_min_competitive_score', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'set_min_competitive_score_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + { + key: 'shallow_advance', + time: 0, + relative: '0.0', + color: '#f5f5f5', + tip: '', + }, + { + key: 'shallow_advance_count', + time: 0, + relative: 0, + color: '#f5f5f5', + tip: '', + }, + ], + selfTime: 0.029634, + timePercentage: '50.73', + absoluteColor: '#fad1d1', + depth: 1, + parent: search1, + time: 0.029634, + lucene: 'DocValuesFieldExistsQuery [field=_primary_term]', + query_type: 'DocValuesFieldExistsQuery', + visible: true, +}; +(search1.children[0] as any) = search1Child; +(searchRoot.treeRoot as any) = search1; +(shard1.searches[0] as any) = searchRoot; + +export const processedResponseWithFirstShard = produce(null, () => [ + { + shards: [shard1], + time: 0.058419, + name: '.kibana_1', + visible: false, + }, +]); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/search_response.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/search_response.ts new file mode 100644 index 00000000000000..b4483cc0fc58e0 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/fixtures/search_response.ts @@ -0,0 +1,308 @@ +/* + * 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 searchResponse: any = [ + { + id: '[L22w_FX2SbqlQYOP5QrYDg] [.apm-agent-configuration] [0]', + searches: [ + { + query: [ + { + type: 'MatchAllDocsQuery', + description: '*:*', + time_in_nanos: 1971, + breakdown: { + set_min_competitive_score_count: 0, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 0, + next_doc: 0, + match: 0, + next_doc_count: 0, + score_count: 0, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 0, + advance_count: 0, + score: 0, + build_scorer_count: 0, + create_weight: 1970, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 0, + }, + }, + ], + rewrite_time: 908, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 1176, + children: [ + { name: 'SimpleTopScoreDocCollector', reason: 'search_top_hits', time_in_nanos: 517 }, + ], + }, + ], + }, + ], + aggregations: [], + }, + { + id: '[L22w_FX2SbqlQYOP5QrYDg] [.kibana_1] [0]', + searches: [ + { + query: [ + { + type: 'ConstantScoreQuery', + description: 'ConstantScore(DocValuesFieldExistsQuery [field=_primary_term])', + time_in_nanos: 58419, + breakdown: { + set_min_competitive_score_count: 0, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 0, + next_doc: 5767, + match: 0, + next_doc_count: 24, + score_count: 24, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 2849, + advance_count: 8, + score: 1431, + build_scorer_count: 16, + create_weight: 8238, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 40061, + }, + children: [ + { + type: 'DocValuesFieldExistsQuery', + description: 'DocValuesFieldExistsQuery [field=_primary_term]', + time_in_nanos: 29634, + breakdown: { + set_min_competitive_score_count: 0, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 0, + next_doc: 2434, + match: 0, + next_doc_count: 24, + score_count: 0, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 1506, + advance_count: 8, + score: 0, + build_scorer_count: 16, + create_weight: 1586, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 24059, + }, + }, + ], + }, + ], + rewrite_time: 14670, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 14706, + children: [ + { + name: 'SimpleTopScoreDocCollector', + reason: 'search_top_hits', + time_in_nanos: 7851, + }, + ], + }, + ], + }, + ], + aggregations: [], + }, + { + id: '[L22w_FX2SbqlQYOP5QrYDg] [.kibana_task_manager_1] [0]', + searches: [ + { + query: [ + { + type: 'ConstantScoreQuery', + description: 'ConstantScore(DocValuesFieldExistsQuery [field=_primary_term])', + time_in_nanos: 30013, + breakdown: { + set_min_competitive_score_count: 0, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 0, + next_doc: 1497, + match: 0, + next_doc_count: 5, + score_count: 3, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 1058, + advance_count: 3, + score: 309, + build_scorer_count: 6, + create_weight: 6727, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 20404, + }, + children: [ + { + type: 'DocValuesFieldExistsQuery', + description: 'DocValuesFieldExistsQuery [field=_primary_term]', + time_in_nanos: 19795, + breakdown: { + set_min_competitive_score_count: 0, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 0, + next_doc: 600, + match: 0, + next_doc_count: 5, + score_count: 0, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 378, + advance_count: 3, + score: 0, + build_scorer_count: 6, + create_weight: 1121, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 17681, + }, + }, + ], + }, + ], + rewrite_time: 10713, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 4924, + children: [ + { + name: 'SimpleTopScoreDocCollector', + reason: 'search_top_hits', + time_in_nanos: 2223, + }, + ], + }, + ], + }, + ], + aggregations: [], + }, + { + id: '[L22w_FX2SbqlQYOP5QrYDg] [.security-7] [0]', + searches: [ + { + query: [ + { + type: 'MatchAllDocsQuery', + description: '*:*', + time_in_nanos: 13254, + breakdown: { + set_min_competitive_score_count: 6, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 3016, + next_doc: 1442, + match: 0, + next_doc_count: 10, + score_count: 10, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 1313, + advance_count: 6, + score: 1169, + build_scorer_count: 12, + create_weight: 1132, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 5137, + }, + }, + ], + rewrite_time: 755, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 18172, + children: [ + { + name: 'SimpleTopScoreDocCollector', + reason: 'search_top_hits', + time_in_nanos: 12507, + }, + ], + }, + ], + }, + ], + aggregations: [], + }, + { + id: '[L22w_FX2SbqlQYOP5QrYDg] [kibana_sample_data_logs] [0]', + searches: [ + { + query: [ + { + type: 'MatchAllDocsQuery', + description: '*:*', + time_in_nanos: 12764, + breakdown: { + set_min_competitive_score_count: 4, + match_count: 0, + shallow_advance_count: 0, + set_min_competitive_score: 2685, + next_doc: 1831, + match: 0, + next_doc_count: 10, + score_count: 10, + compute_max_score_count: 0, + compute_max_score: 0, + advance: 1621, + advance_count: 4, + score: 835, + build_scorer_count: 8, + create_weight: 972, + shallow_advance: 0, + create_weight_count: 1, + build_scorer: 4783, + }, + }, + ], + rewrite_time: 691, + collector: [ + { + name: 'CancellableCollector', + reason: 'search_cancelled', + time_in_nanos: 17700, + children: [ + { + name: 'SimpleTopScoreDocCollector', + reason: 'search_top_hits', + time_in_nanos: 12839, + }, + ], + }, + ], + }, + ], + aggregations: [], + }, +]; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/init_data.test.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/init_data.test.ts new file mode 100644 index 00000000000000..6cd19947a26bc3 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/init_data.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ShardSerialized } from '../../../types'; +import { initDataFor } from '../init_data'; + +import { searchResponse } from './fixtures/search_response'; +import { processedResponseWithFirstShard } from './fixtures/processed_search_response'; + +describe('ProfileTree init data', () => { + test('provides the expected result', () => { + const input: ShardSerialized[] = searchResponse as any; + const actual = initDataFor('searches')(input); + + expect(actual[0].name).toEqual(processedResponseWithFirstShard[0].name); + const expectedFirstShard = processedResponseWithFirstShard[0].shards[0]; + expect(actual[0].shards[0]).toEqual(expectedFirstShard); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/profile_tree.test.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/profile_tree.test.tsx new file mode 100644 index 00000000000000..ca95ac97e260a9 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/profile_tree.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { registerTestBed } from '../../../../../../../../../test_utils'; +import { searchResponse } from './fixtures/search_response'; +import { ProfileTree, Props } from '../profile_tree'; + +describe('ProfileTree', () => { + it('renders', async () => { + const props: Props = { + onHighlight: () => {}, + target: 'searches', + data: searchResponse, + }; + const init = registerTestBed(ProfileTree); + await init(props); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/profile_tree.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/unsafe_utils.test.ts similarity index 52% rename from x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/profile_tree.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/unsafe_utils.test.ts index 6da9a452e5e3c9..17c7051f09769c 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/directives/profile_tree/__tests__/profile_tree.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/unsafe_utils.test.ts @@ -5,21 +5,20 @@ */ import expect from '@kbn/expect'; -import * as util from '../util.js'; -import { normalized, breakdown } from './fixtures/breakdown.js'; -import { inputTimes, normalizedTimes } from './fixtures/normalize_times.js'; -import { inputIndices, normalizedIndices } from './fixtures/normalize_indices.js'; -import { flatTimes } from './fixtures/flatten_times.js'; +import * as util from '../unsafe_utils'; +import { normalized, breakdown } from './fixtures/breakdown'; +import { inputTimes, normalizedTimes } from './fixtures/normalize_times'; +import { inputIndices, normalizedIndices } from './fixtures/normalize_indices'; -describe('normalizeBreakdown', function () { - it('returns correct breakdown', function () { +describe('normalizeBreakdown', function() { + it('returns correct breakdown', function() { const result = util.normalizeBreakdown(breakdown); expect(result).to.eql(normalized); }); }); -describe('normalizeTimes', function () { - it('returns correct normalization', function () { +describe('normalizeTimes', function() { + it('returns correct normalization', function() { const totalTime = 0.447365; // Deep clone the object to preserve the original @@ -31,21 +30,12 @@ describe('normalizeTimes', function () { }); }); -describe('flattenResults', function () { - it('returns correct flattening', function () { - // Deep clone the object to preserve the original - const input = JSON.parse(JSON.stringify(normalizedTimes)); - const flat = []; - util.flattenResults(input, flat, 0, []); - expect(JSON.parse(JSON.stringify(flat))).to.eql(flatTimes); - }); -}); - -describe('normalizeIndices', function () { - it('returns correct ordering', function () { +describe('normalizeIndices', function() { + it('returns correct ordering', function() { // Deep clone the object to preserve the original const input = JSON.parse(JSON.stringify(inputIndices)); - const result = util.normalizeIndices(input, [], 'searches'); + util.normalizeIndices(input, 'searches'); + const result = util.sortIndices(input); expect(result).to.eql(normalizedIndices); }); }); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/utils.test.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/utils.test.ts new file mode 100644 index 00000000000000..3a3d606434241a --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/__tests__/utils.test.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 { hasVisibleChild } from '../utils'; + +describe('Profile Tree utils', () => { + describe('#hasVisibleChild', () => { + it('base case no children', () => { + const op: any = { children: [] }; + expect(hasVisibleChild(op)).toBe(false); + }); + + it('base case with children', () => { + const op: any = { children: [{ visible: false }, { visible: false }, { visible: true }] }; + expect(hasVisibleChild(op)).toBe(true); + }); + + it('is robust', () => { + const op: any = {}; + expect(hasVisibleChild(op)).toBe(false); + }); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/highlight_context.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/highlight_context.tsx new file mode 100644 index 00000000000000..f781d01352f854 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/highlight_context.tsx @@ -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. + */ + +import React, { useContext, createContext, useState } from 'react'; +import { Operation, Shard } from '../../types'; + +const HighlightContext = createContext<{ + selectedRow: string; + setStore: (args: OnHighlightChangeArgs & { id: string }) => void; +}>(null as any); + +export interface OnHighlightChangeArgs { + indexName: string; + shard: Shard; + operation: Operation; +} + +type OnHighlightChangeHandler = (data: OnHighlightChangeArgs) => void; + +export const HighlightContextProvider = ({ + children, + onHighlight, +}: { + children: React.ReactNode; + onHighlight: OnHighlightChangeHandler; +}) => { + const [selectedRow, setSelectedRow] = useState(''); + return ( + { + onHighlight(onHighlightChangeArgs); + setSelectedRow(id); + }, + }} + > + {children} + + ); +}; + +export const useHighlightContext = () => { + const ctx = useContext(HighlightContext); + if (ctx == null) { + throw new Error(`useHighlightContext must be called inside HighlightContext`); + } + return ctx; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index.ts new file mode 100644 index 00000000000000..78efd2cb6687f0 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { ProfileTree } from './profile_tree'; +export { OnHighlightChangeArgs } from './highlight_context'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index_details.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index_details.tsx new file mode 100644 index 00000000000000..20ea7c636ee74f --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/index_details.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { msToPretty } from '../../utils'; +import { Index } from '../../types'; + +export interface Props { + index: Index; +} + +export const IndexDetails = ({ index }: Props) => { + const { time, name } = index; + return ( + + {/* Index Title group */} + + +

    + + {i18n.translate('xpack.searchProfiler.profileTree.indexTitle', { + defaultMessage: 'Index:', + })} + + {' ' + name} +

    +
    +
    + {/* Time details group */} + + + + + {i18n.translate('xpack.searchProfiler.profileTree.cumulativeTimeTitle', { + defaultMessage: 'Cumulative time:', + })} + + + {' ' + msToPretty(time, 3)} + + +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/init_data.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/init_data.ts new file mode 100644 index 00000000000000..d7990b1204b217 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/init_data.ts @@ -0,0 +1,114 @@ +/* + * 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 { produce } from 'immer'; +import { flow } from 'fp-ts/lib/function'; +import { Targets, Shard, ShardSerialized } from '../../types'; +import { calcTimes, normalizeTimes, initTree, normalizeIndices, sortIndices } from './unsafe_utils'; +import { IndexMap } from './types'; + +/** + * Functions prefixed with "mutate" change values by reference. Be careful when using these! + * + * It's recommended to us immer's `produce` functions to ensure immutability. + */ + +export function mutateAggsTimesTree(shard: Shard) { + if (shard.aggregations == null) { + shard.time = 0; + } + let shardTime = 0; + for (const agg of shard.aggregations!) { + const totalTime = calcTimes([agg]); + shardTime += totalTime; + } + for (const agg of shard.aggregations!) { + normalizeTimes([agg], shardTime, 0); + initTree([agg], 0); + } + shard.time = shardTime; +} + +export function mutateSearchTimesTree(shard: Shard) { + if (shard.searches == null) { + shard.time = 0; + } + shard.rewrite_time = 0; + + let shardTime = 0; + for (const search of shard.searches!) { + shard.rewrite_time += search.rewrite_time!; + const totalTime = calcTimes(search.query!); + shardTime += totalTime; + normalizeTimes(search.query!, totalTime, 0); + initTree(search.query!, 0); + search.treeRoot = search.query![0]; + search.query = null as any; + } + shard.time = shardTime; +} + +const initShards = (data: ShardSerialized[]) => + produce(data, draft => { + return draft.map(s => { + const idMatch = s.id.match(/\[([^\]\[]*?)\]/g) || []; + const ids = idMatch.map(id => { + return id.replace('[', '').replace(']', ''); + }); + return { + ...s, + id: ids, + time: 0, + color: '', + relative: 0, + }; + }); + }); + +export const calculateShardValues = (target: Targets) => (data: Shard[]) => + produce(data, draft => { + for (const shard of draft) { + if (target === 'searches') { + mutateSearchTimesTree(shard); + } else if (target === 'aggregations') { + mutateAggsTimesTree(shard); + } + } + }); + +export const initIndices = (data: Shard[]) => + produce(data, doNotChange => { + const indices: IndexMap = {}; + + for (const shard of doNotChange) { + if (!indices[shard.id[1]]) { + indices[shard.id[1]] = { + shards: [], + time: 0, + name: shard.id[1], + visible: false, + }; + } + indices[shard.id[1]].shards.push(shard); + indices[shard.id[1]].time += shard.time; + } + + return indices; + }); + +export const normalize = (target: Targets) => (data: IndexMap) => + produce(data, draft => { + normalizeIndices(draft, target); + }); + +export const initDataFor = (target: Targets) => + flow( + initShards, + calculateShardValues(target), + initIndices, + normalize(target), + sortIndices + ); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/profile_tree.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/profile_tree.tsx new file mode 100644 index 00000000000000..ea24abbdb56db2 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/profile_tree.tsx @@ -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 React, { memo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { IndexDetails } from './index_details'; +import { ShardDetails } from './shard_details'; +import { initDataFor } from './init_data'; +import { Targets, ShardSerialized } from '../../types'; +import { HighlightContextProvider, OnHighlightChangeArgs } from './highlight_context'; + +export interface Props { + target: Targets; + data: ShardSerialized[] | null; + onHighlight: (args: OnHighlightChangeArgs) => void; +} + +export const ProfileTree = memo(({ data, target, onHighlight }: Props) => { + if (!data || data.length === 0) { + return null; + } + + const sortedIndices = initDataFor(target)(data); + + return ( + + + {sortedIndices.map(index => ( + + + + + + + + {index.shards.map(shard => ( + + ))} + + + + ))} + + + ); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/templates/default_query.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/index.ts similarity index 77% rename from x-pack/legacy/plugins/searchprofiler/public/templates/default_query.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/index.ts index afab7e808eb1a2..ea9874d559b378 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/templates/default_query.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/index.ts @@ -4,8 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const defaultQuery = `{ - "query":{ - "match_all" : {} - } -}`; +export { ShardDetails } from './shard_details'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details.tsx new file mode 100644 index 00000000000000..eca2d1994f8c56 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details.tsx @@ -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. + */ + +import React, { useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiLink, EuiIcon } from '@elastic/eui'; + +import { Index, Operation, Shard } from '../../../types'; +import { msToPretty } from '../../../utils'; +import { ShardDetailTree } from './shard_details_tree'; +import { PercentageBadge } from '../../percentage_badge'; + +interface Props { + index: Index; + shard: Shard; + operations: Operation[]; +} + +export const ShardDetails = ({ index, shard, operations }: Props) => { + const { relative, time } = shard; + + const [shardVisibility, setShardVisibility] = useState(false); + + return ( + <> + + + setShardVisibility(!shardVisibility)} + > + [{shard.id[0]}][ + {shard.id[2]}] + + + + + + + + + {shardVisibility + ? operations.map((data, idx) => ( + + )) + : null} + + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree.tsx new file mode 100644 index 00000000000000..d3b0a1a5a6355d --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { ShardDetailsTreeNode } from './shard_details_tree_node'; +import { Index, Operation, Shard } from '../../../types'; + +export interface Props { + data: Operation; + index: Index; + shard: Shard; +} + +export const ShardDetailTree = ({ data, index, shard }: Props) => { + const renderOperations = (operation: Operation): JSX.Element => { + const nextOperation = operation.treeRoot || operation; + return ; + }; + + return ( +
    + + {/* Setting grow to false here is important for IE11. Don't change without testing first! */} + +
    + + {i18n.translate('xpack.searchProfiler.profileTree.header.typeTitle', { + defaultMessage: 'Type and description', + })} + + + {i18n.translate('xpack.searchProfiler.profileTree.header.selfTimeTitle', { + defaultMessage: 'Self time', + })} + + + {i18n.translate('xpack.searchProfiler.profileTree.header.totalTimeTitle', { + defaultMessage: 'Total time', + })} + +
    +
    + + {renderOperations(data)} + +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree_node.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree_node.tsx new file mode 100644 index 00000000000000..75da10f8aca2ef --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/shard_details/shard_details_tree_node.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { EuiLink, EuiBadge, EuiCodeBlock, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { hasVisibleChild } from '../utils'; +import { useHighlightTreeNode } from '../use_highlight_tree_node'; +import { msToPretty } from '../../../utils'; + +import { PercentageBadge } from '../../percentage_badge'; + +import { Index, Operation, Shard } from '../../../types'; + +export interface Props { + index: Index; + shard: Shard; + operation: Operation; +} + +const TAB_WIDTH_PX = 32; + +const limitString = (string: string, limit: number) => + `${string.slice(0, limit)}${string.length > limit ? '...' : ''}`; + +/** + * This is a component that recursively iterates over data to render out a tree like + * structure in a flatly. + */ +export const ShardDetailsTreeNode = ({ operation, index, shard }: Props) => { + const [childrenVisible, setChildrenVisible] = useState(hasVisibleChild(operation)); + const { highlight, isHighlighted, id } = useHighlightTreeNode(); + + const renderTimeRow = (op: Operation) => ( +
    +
    + {op.hasChildren ? ( + setChildrenVisible(!childrenVisible)} + > + + + {' ' + op.query_type} + + ) : ( + <> + + {' ' + op.query_type} + + )} +
    + {/* Self Time Badge */} +
    + + {msToPretty(op.selfTime || 0, 1)} + +
    + {/* Total Time Badge */} +
    + + {msToPretty(op.time, 1)} + +
    + {/* Time percentage Badge */} +
    + +
    +
    + ); + + return ( + <> +
    + {renderTimeRow(operation)} +
    + + + {limitString(operation.lucene || '', 120)} + + highlight({ indexName: index.name, operation, shard })} + > + {i18n.translate('xpack.searchProfiler.profileTree.body.viewDetailsLabel', { + defaultMessage: 'View details', + })} + + +
    +
    + {childrenVisible && + operation.hasChildren && + operation.children.flatMap((childOp, idx) => ( + + ))} + + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/types.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/types.ts new file mode 100644 index 00000000000000..d38e895f66bbcb --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/types.ts @@ -0,0 +1,9 @@ +/* + * 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 { Index } from '../../types'; + +export type IndexMap = Record; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/unsafe_utils.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/unsafe_utils.ts new file mode 100644 index 00000000000000..fc21c5da37764c --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/unsafe_utils.ts @@ -0,0 +1,221 @@ +/* + * 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 { produce } from 'immer'; +import { i18n } from '@kbn/i18n'; +import tinycolor from 'tinycolor2'; +import _ from 'lodash'; + +import { BreakdownItem, Index, Operation, Shard, Targets } from '../../types'; +import { IndexMap } from './types'; + +export const comparator = (v1: number, v2: number) => { + if (v1 < v2) { + return 1; + } + return v1 > v2 ? -1 : 0; +}; + +function getToolTip(key: string) { + switch (key) { + case 'build_scorer': + return i18n.translate('xpack.searchProfiler.buildScorerTimeDescription', { + defaultMessage: + 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.', + }); + case 'create_weight': + return i18n.translate('xpack.searchProfiler.createWeightTimeDescription', { + defaultMessage: + 'The time taken to create the Weight object, which holds temporary information during scoring.', + }); + case 'next_doc': + return i18n.translate('xpack.searchProfiler.nextDocTimeDescription', { + defaultMessage: 'The time taken to advance the iterator to the next matching document.', + }); + case 'score': + return i18n.translate('xpack.searchProfiler.scoreTimeDescription', { + defaultMessage: 'The time taken in actually scoring the document against the query.', + }); + case 'match': + return i18n.translate('xpack.searchProfiler.matchTimeDescription', { + defaultMessage: + 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).', + }); + case 'advance': + return i18n.translate('xpack.searchProfiler.advanceTimeDescription', { + defaultMessage: 'The time taken to advance the iterator to the next document.', + }); + default: + return ''; + } +} + +export function timeInMilliseconds(data: any) { + if (data.time_in_nanos) { + return data.time_in_nanos / 1000000; + } + + if (typeof data.time === 'string') { + return data.time.replace('ms', ''); + } + + return data.time; +} + +export function calcTimes(data: any[], parentId?: string) { + let totalTime = 0; + // First pass to collect total + for (const child of data) { + totalTime += timeInMilliseconds(child); + + child.breakdown = normalizeBreakdown(child.breakdown); + + let childrenTime = 0; + if (child.children != null && child.children.length !== 0) { + childrenTime = calcTimes(child.children, child.id)!; + child.hasChildren = true; + } + child.selfTime = timeInMilliseconds(child) - childrenTime; + } + return totalTime; +} + +export function normalizeBreakdown(breakdown: Record) { + const final: BreakdownItem[] = []; + const total = Object.keys(breakdown).reduce((partialTotal, currentKey) => { + if (currentKey.indexOf('_count') === -1) { + partialTotal += breakdown[currentKey]; + } + return partialTotal; + }, 0); + Object.keys(breakdown) + .sort() + .forEach(key => { + let relative = 0; + if (key.indexOf('_count') === -1) { + relative = ((breakdown[key] / total) * 100).toFixed(1) as any; + } + final.push({ + key, + time: breakdown[key], + relative, + color: tinycolor.mix('#F5F5F5', '#FFAFAF', relative).toHexString(), + tip: getToolTip(key), + }); + }); + + // Sort by time descending and then key ascending + return final.sort((a, b) => { + if (comparator(a.time, b.time) !== 0) { + return comparator(a.time, b.time); + } + + return -1 * comparator(a.key as any, b.key as any); + }); +} + +export function normalizeTimes(data: any[], totalTime: number, depth: number) { + // Second pass to normalize + for (const child of data) { + child.timePercentage = ((timeInMilliseconds(child) / totalTime) * 100).toFixed(2); + child.absoluteColor = tinycolor.mix('#F5F5F5', '#FFAFAF', child.timePercentage).toHexString(); + child.depth = depth; + + if (child.children != null && child.children.length !== 0) { + normalizeTimes(child.children, totalTime, depth + 1); + } + } + + data.sort((a, b) => comparator(timeInMilliseconds(a), timeInMilliseconds(b))); +} + +export function normalizeIndices(indices: IndexMap, target: Targets) { + // Sort the shards per-index + let sortQueryComponents; + if (target === 'searches') { + sortQueryComponents = (a: Shard, b: Shard) => { + const aTime = _.sum(a.searches!, search => { + return search.treeRoot!.time; + }); + const bTime = _.sum(b.searches!, search => { + return search.treeRoot!.time; + }); + + return comparator(aTime, bTime); + }; + } else if (target === 'aggregations') { + sortQueryComponents = (a: Shard, b: Shard) => { + const aTime = _.sum(a.aggregations!, agg => { + return agg.treeRoot!.time; + }); + const bTime = _.sum(b.aggregations!, agg => { + return agg.treeRoot!.time; + }); + + return comparator(aTime, bTime); + }; + } + for (const index of Object.values(indices)) { + index.shards.sort(sortQueryComponents); + for (const shard of index.shards) { + shard.relative = ((shard.time / index.time) * 100).toFixed(2); + shard.color = tinycolor.mix('#F5F5F5', '#FFAFAF', shard.relative as any).toHexString(); + } + } +} + +export function initTree(data: Operation[], depth = 0, parent: Operation | null = null) { + for (const child of data) { + // For bwc of older profile responses + if (!child.description) { + child.description = child.lucene!; + child.lucene = null; + + child.type = child.query_type!; + child.query_type = null; + } + + // Use named function for tests. + child.parent = parent; + child.time = timeInMilliseconds(child); + child.lucene = child.description; + child.query_type = child.type!.split('.').pop()!; + child.visible = child.timePercentage > 20; + child.depth = depth; + + if (child.children != null && child.children.length !== 0) { + initTree(child.children, depth + 1, child); + } + } +} + +export function closeNode(node: Operation) { + const closeDraft = (draft: Operation) => { + draft.visible = false; + + if (draft.children == null || draft.children.length === 0) { + return; + } + + for (const child of draft.children) { + closeDraft(child); + } + }; + return produce(node, draft => { + closeDraft(draft); + }); +} + +export const sortIndices = (data: IndexMap) => + produce(data, doNotChange => { + const sortedIndices: Index[] = []; + for (const index of Object.values(doNotChange)) { + sortedIndices.push(index); + } + // And now sort the indices themselves + sortedIndices.sort((a, b) => comparator(a.time, b.time)); + return sortedIndices; + }); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/use_highlight_tree_node.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/use_highlight_tree_node.ts new file mode 100644 index 00000000000000..6e6f16a2cb683a --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/use_highlight_tree_node.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useRef } from 'react'; +import uuid from 'uuid'; + +import { useHighlightContext, OnHighlightChangeArgs } from './highlight_context'; + +export const useHighlightTreeNode = () => { + const idRef = useRef(uuid.v4()); + const { selectedRow, setStore } = useHighlightContext(); + + const highlight = (value: OnHighlightChangeArgs) => { + setStore({ id: idRef.current, ...value }); + }; + + const isHighlighted = () => { + return selectedRow === idRef.current; + }; + + return { + id: idRef.current, + highlight, + isHighlighted, + }; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/utils.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/utils.ts new file mode 100644 index 00000000000000..d0bedd873a1bd2 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/profile_tree/utils.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Operation } from '../../types'; + +export const hasVisibleChild = ({ children }: Operation) => { + return Boolean(children && children.some(child => child.visible)); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.test.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.test.ts new file mode 100644 index 00000000000000..8ee43e28bcd2c5 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.test.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. + */ + +import { registerTestBed } from '../../../../../../../test_utils'; + +import { SearchProfilerTabs, Props } from './searchprofiler_tabs'; + +describe('Search Profiler Tabs', () => { + it('renders', async () => { + const props: Props = { + activateTab: () => {}, + activeTab: null, + has: { + aggregations: true, + searches: true, + }, + }; + const init = registerTestBed(SearchProfilerTabs); + await init(props); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.tsx new file mode 100644 index 00000000000000..19224e7099fd66 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/components/searchprofiler_tabs.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiTabs, EuiTab } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { Targets } from '../types'; + +export interface Props { + activeTab: Targets | null; + activateTab: (target: Targets) => void; + has: { + searches: boolean; + aggregations: boolean; + }; +} + +export const SearchProfilerTabs = ({ activeTab, activateTab, has }: Props) => { + return ( + + activateTab('searches')} + > + {i18n.translate('xpack.searchProfiler.queryProfileTabTitle', { + defaultMessage: 'Query Profile', + })} + + activateTab('aggregations')} + > + {i18n.translate('xpack.searchProfiler.aggregationProfileTabTitle', { + defaultMessage: 'Aggregation Profile', + })} + + + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/index.ts new file mode 100644 index 00000000000000..1830a34e93d309 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { Main } from './main'; +export { ProfileQueryEditor } from './profile_query_editor'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.test.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.test.tsx new file mode 100644 index 00000000000000..4f17d0b4304f64 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.test.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 { registerTestBed } from '../../../../../../../../../test_utils'; +import { EmptyTreePlaceHolder } from '.'; + +describe('EmptyTreePlaceholder', () => { + it('renders', async () => { + const init = registerTestBed(EmptyTreePlaceHolder); + await init(); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.tsx new file mode 100644 index 00000000000000..bf27620dcac181 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/empty_tree_placeholder.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export const EmptyTreePlaceHolder = () => { + return ( +
    + + {/* TODO: translations */} +

    + {i18n.translate('xpack.searchProfiler.emptyProfileTreeTitle', { + defaultMessage: 'Nothing to see here yet.', + })} +

    +

    + {i18n.translate('xpack.searchProfiler.emptyProfileTreeDescription', { + defaultMessage: + 'Enter a query and press the "Profile" button or provide profile data in the editor.', + })} +

    +
    +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/index.ts new file mode 100644 index 00000000000000..e8b7450ed96453 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { EmptyTreePlaceHolder } from './empty_tree_placeholder'; +export { ProfileLoadingPlaceholder } from './profile_loading_placeholder'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.test.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.test.tsx new file mode 100644 index 00000000000000..9b3348b4cab4b2 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.test.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { registerTestBed } from '../../../../../../../../../test_utils'; +import { ProfileLoadingPlaceholder } from '.'; + +describe('Profile Loading Placeholder', () => { + it('renders', async () => { + const init = registerTestBed(ProfileLoadingPlaceholder); + await init(); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.tsx new file mode 100644 index 00000000000000..fb09c6cddf70a4 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/components/profile_loading_placeholder.tsx @@ -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 React from 'react'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +export const ProfileLoadingPlaceholder = () => { + return ( +
    + +

    + {i18n.translate('xpack.searchProfiler.profilingLoaderText', { + defaultMessage: 'Profiling...', + })} +

    +
    +
    + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/index.ts new file mode 100644 index 00000000000000..509a15c0c71a52 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/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 { Main } from './main'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/main.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/main.tsx new file mode 100644 index 00000000000000..7f5d223949e610 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/main/main.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback } from 'react'; +import _ from 'lodash'; + +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, +} from '@elastic/eui'; +import { ProfileQueryEditor } from '../'; + +import { + SearchProfilerTabs, + ProfileTree, + HighlightDetailsFlyout, + LicenseWarningNotice, +} from '../../components'; + +import { useAppContext } from '../../contexts/app_context'; + +import { EmptyTreePlaceHolder, ProfileLoadingPlaceholder } from './components'; +import { Targets, ShardSerialized } from '../../types'; +import { useProfilerActionContext, useProfilerReadContext } from '../../contexts/profiler_context'; + +function hasSearch(profileResponse: ShardSerialized[]) { + const aggs = _.get(profileResponse, '[0].searches', []); + return aggs.length > 0; +} + +function hasAggregations(profileResponse: ShardSerialized[]) { + const aggs = _.get(profileResponse, '[0].aggregations', []); + return aggs.length > 0; +} + +export const Main = () => { + const { licenseEnabled } = useAppContext(); + + const { + activeTab, + currentResponse, + highlightDetails, + pristine, + profiling, + } = useProfilerReadContext(); + const dispatch = useProfilerActionContext(); + + const setActiveTab = useCallback( + (target: Targets) => dispatch({ type: 'setActiveTab', value: target }), + [dispatch] + ); + + const onHighlight = useCallback(value => dispatch({ type: 'setHighlightDetails', value }), [ + dispatch, + ]); + + const renderLicenseWarning = () => { + return !licenseEnabled ? ( + <> + + + + ) : null; + }; + + const renderProfileTreeArea = () => { + if (profiling) { + return ; + } + + if (activeTab) { + return ( +
    + +
    + ); + } + + if (licenseEnabled && pristine) { + return ; + } + + return null; + }; + + return ( + <> + + + {renderLicenseWarning()} + + + + + + + + + + {renderProfileTreeArea()} + + + + {highlightDetails ? ( + dispatch({ type: 'setHighlightDetails', value: null })} + /> + ) : null} + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/profile_query_editor.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/profile_query_editor.tsx new file mode 100644 index 00000000000000..ea3421ffe89aa8 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/containers/profile_query_editor.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useRef, memo, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiForm, + EuiFieldText, + EuiFormRow, + EuiButton, + EuiText, + EuiFlexGroup, + EuiSpacer, + EuiFlexItem, +} from '@elastic/eui'; +import { Editor, EditorInstance } from '../editor'; +import { useRequestProfile } from '../hooks'; +import { useAppContext } from '../contexts/app_context'; +import { useProfilerActionContext } from '../contexts/profiler_context'; + +const DEFAULT_INDEX_VALUE = '_all'; + +const INITIAL_EDITOR_VALUE = `{ + "query":{ + "match_all" : {} + } +}`; + +/** + * This component should only need to render once. + * + * Drives state changes for mine via profiler action context. + */ +export const ProfileQueryEditor = memo(() => { + const editorRef = useRef(null as any); + const indexInputRef = useRef(null as any); + + const dispatch = useProfilerActionContext(); + + const { licenseEnabled, notifications } = useAppContext(); + const requestProfile = useRequestProfile(); + + const handleProfileClick = async () => { + dispatch({ type: 'setProfiling', value: true }); + try { + const { current: editor } = editorRef; + const { data: result, error } = await requestProfile({ + query: editorRef.current.getValue(), + index: indexInputRef.current.value, + }); + if (error) { + notifications.addDanger(error); + editor.focus(); + return; + } + if (result === null) { + return; + } + dispatch({ type: 'setCurrentResponse', value: result }); + } finally { + dispatch({ type: 'setProfiling', value: false }); + } + }; + + const onEditorReady = useCallback(editorInstance => (editorRef.current = editorInstance), []); + + return ( + + {/* Form */} + + + + + + { + indexInputRef.current = ref!; + ref!.value = DEFAULT_INDEX_VALUE; + }} + /> + + + + + + + {/* Editor */} + + + + + {/* Button */} + + + + + + + handleProfileClick()}> + + {i18n.translate('xpack.searchProfiler.formProfileButtonLabel', { + defaultMessage: 'Profile', + })} + + + + + + + ); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/app_context.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/app_context.tsx new file mode 100644 index 00000000000000..7f8a6cf01dcb27 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/app_context.tsx @@ -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. + */ + +import React, { useContext, createContext } from 'react'; +import { HttpSetup, ToastsSetup } from 'kibana/public'; + +export interface ContextValue { + http: HttpSetup; + notifications: ToastsSetup; + licenseEnabled: boolean; + formatAngularHttpError: (error: any) => string; +} + +const AppContext = createContext(null as any); + +export const AppContextProvider = ({ + children, + value, +}: { + children: React.ReactNode; + value: ContextValue; +}) => { + return {children}; +}; + +export const useAppContext = () => { + const ctx = useContext(AppContext); + if (ctx == null) { + throw new Error(`useAppContext must be called inside AppContextProvider`); + } + return ctx; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/profiler_context.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/profiler_context.tsx new file mode 100644 index 00000000000000..ad46c61b6cd49d --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/contexts/profiler_context.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext, createContext, Dispatch } from 'react'; +import { useStore, State, Action } from '../store'; + +const ProfilerReadContext = createContext(null as any); +const ProfilerActionContext = createContext>(null as any); + +export const ProfileContextProvider = ({ children }: { children: React.ReactNode }) => { + const { dispatch, state } = useStore(); + return ( + + {children} + + ); +}; + +export const useProfilerReadContext = () => { + const ctx = useContext(ProfilerReadContext); + if (ctx == null) { + throw new Error(`useProfilerReadContext must be called inside ProfilerReadContext`); + } + return ctx; +}; + +export const useProfilerActionContext = () => { + const ctx = useContext(ProfilerActionContext); + if (ctx == null) { + throw new Error(`useProfilerActionContext must be called inside ProfilerActionContext`); + } + return ctx; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.test.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.test.tsx new file mode 100644 index 00000000000000..d6702ef080ab73 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.test.tsx @@ -0,0 +1,27 @@ +/* + * 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 'brace'; +import 'brace/mode/json'; + +jest.mock('./worker', () => { + return { workerModule: { id: 'ace/mode/json_worker', src: '' } }; +}); + +import { registerTestBed } from '../../../../../../../test_utils'; +import { Editor, Props } from '.'; + +describe('Editor Component', () => { + it('renders', async () => { + const props: Props = { + initialValue: '', + licenseEnabled: true, + onEditorReady: e => {}, + }; + // Ignore the warning about Worker not existing for now... + const init = registerTestBed(Editor); + await init(props); + }); +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.tsx new file mode 100644 index 00000000000000..014336f379059a --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/editor.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo, useRef, useEffect, useState } from 'react'; +import { Editor as AceEditor } from 'brace'; + +import { initializeEditor } from './init_editor'; +import { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode'; + +interface EditorShim { + getValue(): string; + focus(): void; +} + +export type EditorInstance = EditorShim; + +export interface Props { + licenseEnabled: boolean; + initialValue: string; + onEditorReady: (editor: EditorShim) => void; +} + +const createEditorShim = (aceEditor: AceEditor): EditorShim => { + return { + getValue() { + return aceEditor.getValue(); + }, + focus() { + aceEditor.focus(); + }, + }; +}; + +export const Editor = memo(({ licenseEnabled, initialValue, onEditorReady }: Props) => { + const containerRef = useRef(null as any); + const editorInstanceRef = useRef(null as any); + + const [textArea, setTextArea] = useState(null); + + if (licenseEnabled) { + useUIAceKeyboardMode(textArea); + } + + useEffect(() => { + const divEl = containerRef.current; + editorInstanceRef.current = initializeEditor({ el: divEl, licenseEnabled }); + editorInstanceRef.current.setValue(initialValue, 1); + setTextArea(containerRef.current!.querySelector('textarea')); + + onEditorReady(createEditorShim(editorInstanceRef.current)); + }, [initialValue, onEditorReady, licenseEnabled]); + + return
    ; +}); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/index.ts new file mode 100644 index 00000000000000..f17f37923ae837 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/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 { Editor, Props, EditorInstance } from './editor'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/init_editor.ts similarity index 85% rename from x-pack/legacy/plugins/searchprofiler/public/editor/index.ts rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/init_editor.ts index 45562385c2e691..e5aad16bc4af20 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/editor/index.ts +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/init_editor.ts @@ -10,11 +10,9 @@ import { installXJsonMode } from './x_json_mode'; export function initializeEditor({ el, licenseEnabled, - initialContent, }: { el: HTMLDivElement; licenseEnabled: boolean; - initialContent: string; }) { const editor: ace.Editor = ace.acequire('ace/ace').edit(el); @@ -25,6 +23,10 @@ export function initializeEditor({ editor.setReadOnly(true); editor.container.style.pointerEvents = 'none'; editor.container.style.opacity = '0.5'; + const textArea = editor.container.querySelector('textarea'); + if (textArea) { + textArea.setAttribute('tabindex', '-1'); + } editor.renderer.setStyle('disabled'); editor.blur(); } diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/use_ui_ace_keyboard_mode.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/use_ui_ace_keyboard_mode.tsx new file mode 100644 index 00000000000000..edf31c2e7c07f8 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/use_ui_ace_keyboard_mode.tsx @@ -0,0 +1,110 @@ +/* + * 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. + */ + +/** + * Copied from Console plugin + */ + +import React, { useEffect, useRef } from 'react'; +import * as ReactDOM from 'react-dom'; +import { keyCodes, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +const OverlayText = () => ( + // The point of this element is for accessibility purposes, so ignore eslint error + // in this case + // + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + <> + + {i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionEnter', { + defaultMessage: 'Press Enter to start editing.', + })} + + + {i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionExit', { + defaultMessage: `When you are done, press Escape to stop editing.`, + })} + + +); + +export function useUIAceKeyboardMode(aceTextAreaElement: HTMLTextAreaElement | null) { + const overlayMountNode = useRef(null); + const autoCompleteVisibleRef = useRef(false); + + useEffect(() => { + function onDismissOverlay(event: KeyboardEvent) { + if (event.keyCode === keyCodes.ENTER) { + event.preventDefault(); + aceTextAreaElement!.focus(); + } + } + + function enableOverlay() { + if (overlayMountNode.current) { + overlayMountNode.current.focus(); + } + } + + const isAutoCompleteVisible = () => { + const autoCompleter = document.querySelector('.ace_autocomplete'); + if (!autoCompleter) { + return false; + } + // The autoComplete is just hidden when it's closed, not removed from the DOM. + return autoCompleter.style.display !== 'none'; + }; + + const documentKeyDownListener = () => { + autoCompleteVisibleRef.current = isAutoCompleteVisible(); + }; + + const aceKeydownListener = (event: KeyboardEvent) => { + if (event.keyCode === keyCodes.ESCAPE && !autoCompleteVisibleRef.current) { + event.preventDefault(); + event.stopPropagation(); + enableOverlay(); + } + }; + + if (aceTextAreaElement) { + // We don't control HTML elements inside of ace so we imperatively create an element + // that acts as a container and insert it just before ace's textarea element + // so that the overlay lives at the correct spot in the DOM hierarchy. + overlayMountNode.current = document.createElement('div'); + overlayMountNode.current.className = 'kbnUiAceKeyboardHint'; + overlayMountNode.current.setAttribute('role', 'application'); + overlayMountNode.current.tabIndex = 0; + overlayMountNode.current.addEventListener('focus', enableOverlay); + overlayMountNode.current.addEventListener('keydown', onDismissOverlay); + + ReactDOM.render(, overlayMountNode.current); + + aceTextAreaElement.parentElement!.insertBefore(overlayMountNode.current, aceTextAreaElement); + aceTextAreaElement.setAttribute('tabindex', '-1'); + + // Order of events: + // 1. Document capture event fires first and we check whether an autocomplete menu is open on keydown + // (not ideal because this is scoped to the entire document). + // 2. Ace changes it's state (like hiding or showing autocomplete menu) + // 3. We check what button was pressed and whether autocomplete was visible then determine + // whether it should act like a dismiss or if we should display an overlay. + document.addEventListener('keydown', documentKeyDownListener, { capture: true }); + aceTextAreaElement.addEventListener('keydown', aceKeydownListener); + } + return () => { + if (aceTextAreaElement) { + document.removeEventListener('keydown', documentKeyDownListener); + aceTextAreaElement.removeEventListener('keydown', aceKeydownListener); + const textAreaContainer = aceTextAreaElement.parentElement; + if (textAreaContainer && textAreaContainer.contains(overlayMountNode.current!)) { + textAreaContainer.removeChild(overlayMountNode.current!); + } + } + }; + }, [aceTextAreaElement]); +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/worker/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/index.ts similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/editor/worker/index.ts rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/index.ts diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/worker/worker.d.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/worker.d.ts similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/editor/worker/worker.d.ts rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/worker.d.ts diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/worker/worker.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/worker.js similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/editor/worker/worker.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/worker/worker.js diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/x_json_highlight_rules.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/x_json_highlight_rules.ts similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/editor/x_json_highlight_rules.ts rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/x_json_highlight_rules.ts diff --git a/x-pack/legacy/plugins/searchprofiler/public/editor/x_json_mode.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/x_json_mode.ts similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/editor/x_json_mode.ts rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/editor/x_json_mode.ts diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/index.ts new file mode 100644 index 00000000000000..a9fa5fee994e0f --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/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 { useRequestProfile } from './use_request_profile'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/use_request_profile.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/use_request_profile.ts new file mode 100644 index 00000000000000..34b49be7dc39c6 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/hooks/use_request_profile.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 { i18n } from '@kbn/i18n'; + +import { useAppContext } from '../contexts/app_context'; +import { checkForParseErrors } from '../utils'; +import { ShardSerialized } from '../types'; + +interface Args { + query: string; + index: string; +} + +interface ReturnValue { + data: ShardSerialized[] | null; + error?: string; +} + +export const useRequestProfile = () => { + const { http, notifications, formatAngularHttpError, licenseEnabled } = useAppContext(); + return async ({ query, index }: Args): Promise => { + if (!licenseEnabled) { + return { data: null }; + } + const { error, parsed } = checkForParseErrors(query); + if (error) { + notifications.addError(error, { + title: i18n.translate('xpack.searchProfiler.errorToastTitle', { + defaultMessage: 'JSON parse error', + }), + }); + return { data: null }; + } + // Shortcut the network request if we have json with shards already... + if (parsed.profile && parsed.profile.shards) { + return { data: parsed.profile.shards }; + } + + const payload: Record = { query }; + + if (index == null || index === '') { + payload.index = '_all'; + } else { + payload.index = index; + } + + try { + const resp = await http.post('../api/searchprofiler/profile', { + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + }); + + if (!resp.ok) { + return { data: null, error: resp.err.msg }; + } + + return { data: resp.resp.profile.shards }; + } catch (e) { + try { + // Is this a known error type? + const errorString = formatAngularHttpError(e); + notifications.addError(e, { title: errorString }); + } catch (_) { + // Otherwise just report the original error + notifications.addError(e, { + title: i18n.translate('xpack.searchProfiler.errorSomethingWentWrongTitle', { + defaultMessage: 'Something went wrong', + }), + }); + } + return { data: null }; + } + }; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/index.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.scss similarity index 84% rename from x-pack/legacy/plugins/searchprofiler/public/index.scss rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.scss index 2db81310a8db48..e04e81c023196a 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/index.scss +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.scss @@ -9,5 +9,4 @@ // prfDevTool__cell // prfDevTool__shardDetails -@import 'directives/index'; -@import 'templates/index'; \ No newline at end of file +@import 'styles/index'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.tsx b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.tsx new file mode 100644 index 00000000000000..d29f193ce9d90d --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/index.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { Main } from './containers'; +import { AppContextProvider } from './contexts/app_context'; +import { ProfileContextProvider } from './contexts/profiler_context'; + +import { AppDependencies } from './boot'; + +export function App({ + I18nContext, + licenseEnabled, + notifications, + http, + formatAngularHttpError, +}: AppDependencies) { + return ( + + + +
    + + + + ); +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/index.ts new file mode 100644 index 00000000000000..5e30e07a7b8b73 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { useStore, State } from './store'; +export { Action } from './reducer'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/reducer.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/reducer.ts new file mode 100644 index 00000000000000..dac9dab9bd092f --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/reducer.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { produce } from 'immer'; +import { Reducer } from 'react'; +import { State } from './store'; + +import { OnHighlightChangeArgs } from '../components/profile_tree'; +import { ShardSerialized, Targets } from '../types'; + +export type Action = + | { type: 'setPristine'; value: boolean } + | { type: 'setProfiling'; value: boolean } + | { type: 'setHighlightDetails'; value: OnHighlightChangeArgs | null } + | { type: 'setActiveTab'; value: Targets | null } + | { type: 'setCurrentResponse'; value: ShardSerialized[] | null }; + +export const reducer: Reducer = (state, action) => + produce(state, draft => { + if (action.type === 'setPristine') { + draft.pristine = action.value; + return; + } + + if (action.type === 'setProfiling') { + draft.profiling = action.value; + if (draft.profiling) { + draft.currentResponse = null; + draft.highlightDetails = null; + } + return; + } + + if (action.type === 'setHighlightDetails') { + draft.highlightDetails = action.value; + return; + } + + if (action.type === 'setActiveTab') { + draft.activeTab = action.value; + return; + } + + if (action.type === 'setCurrentResponse') { + draft.currentResponse = action.value; + if (draft.currentResponse) { + // Default to the searches tab + draft.activeTab = 'searches'; + } + return; + } + + throw new Error(`Unknown action: ${action}`); + }); diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/store.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/store.ts new file mode 100644 index 00000000000000..7b5a1ce93583d6 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/store/store.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { useReducer } from 'react'; +import { reducer } from './reducer'; +import { ShardSerialized, Targets } from '../types'; +import { OnHighlightChangeArgs } from '../components/profile_tree'; + +export interface State { + profiling: boolean; + pristine: boolean; + highlightDetails: OnHighlightChangeArgs | null; + activeTab: Targets | null; + currentResponse: ShardSerialized[] | null; +} + +export const initialState: State = { + profiling: false, + pristine: false, + highlightDetails: null, + activeTab: null, + currentResponse: null, +}; + +export const useStore = () => { + const [state, dispatch] = useReducer(reducer, initialState); + return { + state, + dispatch, + }; +}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/_index.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/_index.scss new file mode 100644 index 00000000000000..a72d079354f897 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/_index.scss @@ -0,0 +1,83 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/panel/mixins'; + +@import 'mixins'; + +@import 'components/highlight_details_flyout'; +@import 'components/profile_tree'; +@import 'components/percentage_badge'; + +@import 'containers/main'; +@import 'containers/profile_query_editor'; + +#searchProfilerAppRoot { + height: 100%; + display: flex; + flex: 1 1 auto; +} + +.prfDevTool__licenseWarning { + &__container { + max-width: 1000px; + } +} + + +.prfDevTool__page { + height: 100%; + display: flex; + flex: 1 1 auto; + + &__body { + height: 100%; + flex: 1 1 auto; + } + + &__pageBody { + height: 100%; + flex: 1 1 auto; + } + + &__pageBodyContent { + height: 100%; + } + + &__pageBodyContentBody { + height: 100%; + } + + &__pageContentBodyContent { + height: 100%; + } + + &__bodyGroup { + height: 100%; + } +} + +.prfDevTool { + height: calc(100vh - #{$euiHeaderChildSize}); + overflow: hidden; + + .devApp__container { + height: 100%; + overflow: hidden; + flex-shrink: 1; + } + + &__container { + overflow: hidden; + } +} + +.prfDevTool__detail { + font-size: $euiFontSizeS; + padding-left: $euiSizeL - 3px; // Alignment is weird + margin-bottom: $euiSizeS; + display: flex; + justify-content: space-between; + + .euiLink { + flex-shrink: 0; + } +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/directives/_mixins.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/_mixins.scss similarity index 100% rename from x-pack/legacy/plugins/searchprofiler/public/directives/_mixins.scss rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/_mixins.scss diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_highlight_details_flyout.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_highlight_details_flyout.scss new file mode 100644 index 00000000000000..4b36e9833979d8 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_highlight_details_flyout.scss @@ -0,0 +1,8 @@ +.prfDevTool__flyoutSubtitle { + margin-bottom: $euiSizeS; + display: inline-block; +} + +.prfDevTool__details { + max-width: map-get($euiBreakpoints, 's'); +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_percentage_badge.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_percentage_badge.scss new file mode 100644 index 00000000000000..204ca3da17574b --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_percentage_badge.scss @@ -0,0 +1,14 @@ +.prfDevTool__percentBadge { + &__progress--percent { + @include prfDevToolProgress; + width: $badgeSize; + } + + &__progress--time { + @include prfDevToolProgress(#FFAFAF); + background-color: #F5F5F5; // Must be light at all times + // Width of 3 badges, with the last child not having padding on the left + width: ($badgeSize * 3) - ($euiSizeXS * .75); + // Force text to always be dark on top of white -> pink color + } +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_profile_tree.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_profile_tree.scss new file mode 100644 index 00000000000000..cc4d334f58fd33 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/components/_profile_tree.scss @@ -0,0 +1,99 @@ + +$badgeSize: $euiSize * 5.5; + +// Profile details treeview + +.prfDevTool__profileTree { + + &__container { + height: 100%; + } + + &__shardDetails--dim small { + color: $euiColorDarkShade; + } + + &__shardDetails { + line-height: 1; + overflow-wrap: break-word; + + h3 { + font-size: $euiSize; + } + + &:disabled { + text-decoration: none !important; + cursor: default; + } + } + + &__shard { + border: none; + + &__header-flex-item { + align-self: center; + } + } + + &__index { + width: 100%; + padding: $euiSize $euiSizeS; + } + + &__indexDetails { + align-self: center; + } + + &__tvRow--last { + cursor: pointer; + } + + &__tvRow, + &__tvHeader { + display: table; + width: 100%; + table-layout: fixed; + } + + &__tvHeader { + @include euiFontSizeXS; + color: $euiColorDarkShade; + } + + &__time, + &__totalTime, + &__percentage { + width: $badgeSize; + } + + // BADGES (and those used for progress) + &__badge { + border: none; + display: block; + // Force text to always be dark on top of white -> pink color + color: lightOrDarkTheme($euiColorDarkestShade, $euiColorLightestShade); + } + + &__panel { + border-bottom: $euiBorderThin; + } + + &__panelBody { + margin-top: $euiSizeS; + margin-left: $euiSizeL; + } + + &__cell { + display: table-cell; + vertical-align: middle; + padding: $euiSizeXS; + + &:first-of-type { + padding-left: 0; + } + + &:last-of-type { + padding-right: 0; + } + } +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_main.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_main.scss new file mode 100644 index 00000000000000..09bcddef02cc34 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_main.scss @@ -0,0 +1,62 @@ + +@include euiBreakpoint('xs', 's') { + .prfDevTool__shardDetailsWrapper { + flex-direction: column; + align-items: flex-start; + } +} + +.prfDevTool__main { + height: 100%; + order: 2; + margin-left: $euiSize; + display: flex; + overflow: hidden; + flex-direction: column; + + // Make only the tab content scroll + .search-profiler-tabs { + flex-shrink: 0; + } + + &__profiletree { + height: 100%; + overflow-y: auto; + flex-grow: 1; + } + + &__emptyTreePlaceholder { + h1 { + font-size: $euiSizeL; + color: $euiColorMediumShade; + } + p { + font-size: $euiSize; + color: $euiColorMediumShade; + } + h1, p { + cursor: default; + user-select: none; + } + // Slight offset from the top. + margin: 5% 0 auto; + text-align: center; + } +} + +@include euiPanel('prfDevTool__main'); + +@include euiBreakpoint('xs', 's') { + .prfDevTool__container { + flex-direction: column; + } + + .prfDevTool__main { + flex: 0 0 auto; + } + + .prfDevTool__main { + margin: $euiSize 0; + } +} + diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_profile_query_editor.scss b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_profile_query_editor.scss new file mode 100644 index 00000000000000..035ff16c990bb1 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/styles/containers/_profile_query_editor.scss @@ -0,0 +1,25 @@ + +.prfDevTool__sense { + order: 1; + // To anchor ace editor + position: relative; + + // Ace Editor overrides + .ace_editor { + min-height: $euiSize * 10; + flex-grow: 1; + margin-bottom: $euiSize; + margin-top: $euiSize; + outline: solid 1px $euiColorLightShade; + } + + .errorMarker { + position: absolute; + background: rgba($euiColorDanger, .5); + z-index: 20; + } +} + +.prfDevTool__profileButtonContainer { + flex-shrink: 1; +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/types.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/types.ts new file mode 100644 index 00000000000000..2b4dc01c45d6fc --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/types.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface BreakdownItem { + tip: string; + key: string; + time: number; + color: string; + relative: number; +} + +export type Targets = 'searches' | 'aggregations'; + +export interface Shard { + id: string[]; + relative: number | string; + time: number; + color: string; + aggregations?: Operation[]; + searches?: Operation[]; + rewrite_time?: number; +} + +export interface Index { + name: string; + time: number; + shards: Shard[]; + visible: boolean; +} + +export interface ShardSerialized { + id: string; + searches: Operation[]; + aggregations: Operation[]; +} + +export interface Operation { + description?: string; + hasChildren: boolean; + visible: boolean; + selfTime: number; + timePercentage: number; + absoluteColor: string; + time: number; + + parent: Operation | null; + children: Operation[]; + + // Only exists on top level + treeRoot?: Operation; + + depth?: number; + + // BWC + type?: string; + + lucene: string | null; + query_type: string | null; + + // Searches + query?: any[]; + rewrite_time?: number; + + // Aggregations + breakdown?: any; +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/__tests__/app_util.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.test.ts similarity index 61% rename from x-pack/legacy/plugins/searchprofiler/public/__tests__/app_util.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.test.ts index 164e7394dead71..9dece5a39e96c3 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/__tests__/app_util.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.test.ts @@ -5,18 +5,18 @@ */ import expect from '@kbn/expect'; -import { checkForParseErrors } from '../app_util.js'; +import { checkForParseErrors } from '.'; -describe('checkForParseErrors', function () { - it('returns false from bad JSON', function () { +describe('checkForParseErrors', function() { + it('returns error from bad JSON', function() { const json = '{"foo": {"bar": {"baz": "buzz}}}'; const result = checkForParseErrors(json); - expect(result.status).to.be(false); + expect(result.error.message).to.be(`Unexpected end of JSON input`); }); - it('returns true from good JSON', function () { + it('returns parsed value from good JSON', function() { const json = '{"foo": {"bar": {"baz": "buzz"}}}'; const result = checkForParseErrors(json); - expect(result.status).to.be(true); + expect(!!result.parsed).to.be(true); }); }); diff --git a/x-pack/legacy/plugins/searchprofiler/public/app_util.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.ts similarity index 71% rename from x-pack/legacy/plugins/searchprofiler/public/app_util.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.ts index 18eef2259bd6ac..4267fd0d2f9019 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/app_util.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/check_for_json_errors.ts @@ -5,18 +5,18 @@ */ // Convert triple quotes into regular quotes and escape internal quotes. -function collapseLiteralStrings(data) { - return data.replace(/"""(?:\s*\r?\n)?((?:.|\r?\n)*?)(?:\r?\n\s*)?"""/g, function (match, literal) { +function collapseLiteralStrings(data: string) { + return data.replace(/"""(?:\s*\r?\n)?((?:.|\r?\n)*?)(?:\r?\n\s*)?"""/g, function(match, literal) { return JSON.stringify(literal); }); } -export function checkForParseErrors(json) { +export function checkForParseErrors(json: string) { const sanitizedJson = collapseLiteralStrings(json); try { const parsedJson = JSON.parse(sanitizedJson); - return { status: true, parsed: parsedJson }; + return { parsed: parsedJson, error: null }; } catch (error) { - return { status: false, error }; + return { error, parsed: null }; } } diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/index.ts new file mode 100644 index 00000000000000..556a03fc96fe35 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { checkForParseErrors } from './check_for_json_errors'; +export { msToPretty } from './ms_to_pretty'; +export { nsToPretty } from './ns_to_pretty'; diff --git a/x-pack/legacy/plugins/searchprofiler/public/filters/ms_to_pretty.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ms_to_pretty.ts similarity index 91% rename from x-pack/legacy/plugins/searchprofiler/public/filters/ms_to_pretty.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ms_to_pretty.ts index aa088ffc46c596..3df47cfdeb1770 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/filters/ms_to_pretty.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ms_to_pretty.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export function msToPretty(ms, precision) { +export function msToPretty(ms: number, precision: number) { if (!precision) { precision = 1; } diff --git a/x-pack/legacy/plugins/searchprofiler/public/filters/__tests__/ns_to_pretty.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.test.ts similarity index 60% rename from x-pack/legacy/plugins/searchprofiler/public/filters/__tests__/ns_to_pretty.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.test.ts index 85208495fc18e2..6c0e1124af176c 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/filters/__tests__/ns_to_pretty.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.test.ts @@ -5,45 +5,45 @@ */ import expect from '@kbn/expect'; -import { nsToPretty } from '../ns_to_pretty.js'; +import { nsToPretty } from './ns_to_pretty'; -describe('nsToPretty', function () { - it('returns correct time for ns', function () { +describe('nsToPretty', function() { + it('returns correct time for ns', function() { const result = nsToPretty(500, 1); expect(result).to.eql('500.0ns'); }); - it('returns correct time for µs', function () { + it('returns correct time for µs', function() { const result = nsToPretty(5000, 1); expect(result).to.eql('5.0µs'); }); - it('returns correct time for ms', function () { + it('returns correct time for ms', function() { const result = nsToPretty(5000000, 1); expect(result).to.eql('5.0ms'); }); - it('returns correct time for s', function () { + it('returns correct time for s', function() { const result = nsToPretty(5000000000, 1); expect(result).to.eql('5.0s'); }); - it('returns correct time for min', function () { + it('returns correct time for min', function() { const result = nsToPretty(5000000000 * 60, 1); expect(result).to.eql('5.0min'); }); - it('returns correct time for hr', function () { - const result = nsToPretty(3.6e+12 * 5, 1); + it('returns correct time for hr', function() { + const result = nsToPretty(3.6e12 * 5, 1); expect(result).to.eql('5.0hr'); }); - it('returns correct time for day', function () { - const result = nsToPretty(3.6e+12 * 24 * 5, 1); + it('returns correct time for day', function() { + const result = nsToPretty(3.6e12 * 24 * 5, 1); expect(result).to.eql('5.0d'); }); - it('returns correct time for precision', function () { + it('returns correct time for precision', function() { const result = nsToPretty(500, 5); expect(result).to.eql('500.00000ns'); }); diff --git a/x-pack/legacy/plugins/searchprofiler/public/filters/ns_to_pretty.js b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.ts similarity index 89% rename from x-pack/legacy/plugins/searchprofiler/public/filters/ns_to_pretty.js rename to x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.ts index 74737ed8e30d4b..bf0b487566f6fe 100644 --- a/x-pack/legacy/plugins/searchprofiler/public/filters/ns_to_pretty.js +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/application/utils/ns_to_pretty.ts @@ -6,7 +6,7 @@ import { msToPretty } from './ms_to_pretty'; -export function nsToPretty(ns, precision) { +export function nsToPretty(ns: number, precision: number) { if (!precision) { precision = 1; } diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/index.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/index.ts new file mode 100644 index 00000000000000..3d77f703b42cd4 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from 'src/core/public'; +import { SearchProfilerUIPlugin } from './plugin'; + +export function plugin(ctx: PluginInitializerContext) { + return new SearchProfilerUIPlugin(ctx); +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/np_ready/plugin.ts b/x-pack/legacy/plugins/searchprofiler/public/np_ready/plugin.ts new file mode 100644 index 00000000000000..ff98e706abf5cd --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/public/np_ready/plugin.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. + */ + +import { + Plugin, + CoreStart, + CoreSetup, + PluginInitializerContext, + ToastsStart, +} from 'src/core/public'; + +import { boot } from './application/boot'; + +export class SearchProfilerUIPlugin implements Plugin { + constructor(ctx: PluginInitializerContext) {} + + async setup( + core: CoreSetup, + plugins: { + __LEGACY: { + I18nContext: any; + licenseEnabled: boolean; + notifications: ToastsStart; + formatAngularHttpError: any; + el: HTMLElement; + }; + } + ) { + const { http } = core; + const { + __LEGACY: { I18nContext, licenseEnabled, notifications, formatAngularHttpError, el }, + } = plugins; + core.application.register({ + id: 'searchprofiler', + title: 'SearchProfiler', + mount(ctx, params) { + return boot({ + http, + licenseEnabled, + el, + I18nContext, + notifications, + formatAngularHttpError, + }); + }, + }); + } + + async start(core: CoreStart, plugins: any) {} + + async stop() {} +} diff --git a/x-pack/legacy/plugins/searchprofiler/public/range.js b/x-pack/legacy/plugins/searchprofiler/public/range.js deleted file mode 100644 index d52ed783d9720a..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/range.js +++ /dev/null @@ -1,257 +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. - */ - - -// Pulled from Ace because I can't for the life of me -// figure out how to import it. This needs to be fixed TODO - -const comparePoints = function (p1, p2) { - return p1.row - p2.row || p1.column - p2.column; -}; - -export function Range(startRow, startColumn, endRow, endColumn) { - this.start = { - row: startRow, - column: startColumn - }; - - this.end = { - row: endRow, - column: endColumn - }; -} - -(function () { - this.isEqual = function (range) { - return this.start.row === range.start.row && - this.end.row === range.end.row && - this.start.column === range.start.column && - this.end.column === range.end.column; - }; - this.toString = function () { - return ('Range: [' + this.start.row + '/' + this.start.column + - '] -> [' + this.end.row + '/' + this.end.column + ']'); - }; - - this.contains = function (row, column) { - return this.compare(row, column) === 0; - }; - this.compareRange = function (range) { - let cmp; - const end = range.end; - const start = range.start; - - cmp = this.compare(end.row, end.column); - if (cmp === 1) { - cmp = this.compare(start.row, start.column); - if (cmp === 1) { - return 2; - } else if (cmp === 0) { - return 1; - } else { - return 0; - } - } else if (cmp === -1) { - return -2; - } else { - cmp = this.compare(start.row, start.column); - if (cmp === -1) { - return -1; - } else if (cmp === 1) { - return 42; - } else { - return 0; - } - } - }; - this.comparePoint = function (p) { - return this.compare(p.row, p.column); - }; - this.containsRange = function (range) { - return this.comparePoint(range.start) === 0 && this.comparePoint(range.end) === 0; - }; - this.intersects = function (range) { - const cmp = this.compareRange(range); - return (cmp === -1 || cmp === 0 || cmp === 1); - }; - this.isEnd = function (row, column) { - return this.end.row === row && this.end.column === column; - }; - this.isStart = function (row, column) { - return this.start.row === row && this.start.column === column; - }; - this.setStart = function (row, column) { - if (typeof row === 'object') { - this.start.column = row.column; - this.start.row = row.row; - } else { - this.start.row = row; - this.start.column = column; - } - }; - this.setEnd = function (row, column) { - if (typeof row === 'object') { - this.end.column = row.column; - this.end.row = row.row; - } else { - this.end.row = row; - this.end.column = column; - } - }; - this.inside = function (row, column) { - if (this.compare(row, column) === 0) { - if (this.isEnd(row, column) || this.isStart(row, column)) { - return false; - } else { - return true; - } - } - return false; - }; - this.insideStart = function (row, column) { - if (this.compare(row, column) === 0) { - if (this.isEnd(row, column)) { - return false; - } else { - return true; - } - } - return false; - }; - this.insideEnd = function (row, column) { - if (this.compare(row, column) === 0) { - if (this.isStart(row, column)) { - return false; - } else { - return true; - } - } - return false; - }; - this.compare = function (row, column) { - if (!this.isMultiLine()) { - if (row === this.start.row) { - if (column < this.start.column) { - return -1; - } - return column > this.end.column ? 1 : 0; - } - } - - if (row < this.start.row) { - return -1; - } - - - if (row > this.end.row) { - return 1; - } - - if (this.start.row === row) { - return column >= this.start.column ? 0 : -1; - } - - if (this.end.row === row) { - return column <= this.end.column ? 0 : 1; - } - - return 0; - }; - this.compareStart = function (row, column) { - if (this.start.row === row && this.start.column === column) { - return -1; - } else { - return this.compare(row, column); - } - }; - this.compareEnd = function (row, column) { - if (this.end.row === row && this.end.column === column) { - return 1; - } else { - return this.compare(row, column); - } - }; - this.compareInside = function (row, column) { - if (this.end.row === row && this.end.column === column) { - return 1; - } else if (this.start.row === row && this.start.column === column) { - return -1; - } else { - return this.compare(row, column); - } - }; - this.clipRows = function (firstRow, lastRow) { - let end; - let start; - if (this.end.row > lastRow) { - end = { row: lastRow + 1, column: 0 }; - } else if (this.end.row < firstRow) { - end = { row: firstRow, column: 0 }; - } - - if (this.start.row > lastRow) { - start = { row: lastRow + 1, column: 0 }; - } else if (this.start.row < firstRow) { - start = { row: firstRow, column: 0 }; - } - return Range.fromPoints(start || this.start, end || this.end); - }; - this.extend = function (row, column) { - const cmp = this.compare(row, column); - - if (cmp === 0) { - return this; - } - let start; - let end; - if (cmp === -1) { - start = { row: row, column: column }; - } else { - end = { row: row, column: column }; - } - return Range.fromPoints(start || this.start, end || this.end); - }; - - this.isEmpty = function () { - return (this.start.row === this.end.row && this.start.column === this.end.column); - }; - this.isMultiLine = function () { - return (this.start.row !== this.end.row); - }; - this.clone = function () { - return Range.fromPoints(this.start, this.end); - }; - this.collapseRows = function () { - if (this.end.column === 0) { - return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row - 1), 0); - } - return new Range(this.start.row, 0, this.end.row, 0); - }; - this.toScreenRange = function (session) { - const screenPosStart = session.documentToScreenPosition(this.start); - const screenPosEnd = session.documentToScreenPosition(this.end); - - return new Range( - screenPosStart.row, screenPosStart.column, - screenPosEnd.row, screenPosEnd.column - ); - }; - this.moveBy = function (row, column) { - this.start.row += row; - this.start.column += column; - this.end.row += row; - this.end.column += column; - }; - -}).call(Range.prototype); -Range.fromPoints = function (start, end) { - return new Range(start.row, start.column, end.row, end.column); -}; -Range.comparePoints = comparePoints; - -Range.comparePoints = function (p1, p2) { - return p1.row - p2.row || p1.column - p2.column; -}; diff --git a/x-pack/legacy/plugins/searchprofiler/public/templates/_index.scss b/x-pack/legacy/plugins/searchprofiler/public/templates/_index.scss deleted file mode 100644 index 495452735ac83e..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/templates/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'templates'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/searchprofiler/public/templates/_templates.scss b/x-pack/legacy/plugins/searchprofiler/public/templates/_templates.scss deleted file mode 100644 index 9acd5cf32051ec..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/templates/_templates.scss +++ /dev/null @@ -1,103 +0,0 @@ -@import '@elastic/eui/src/components/header/variables'; -@import '@elastic/eui/src/components/panel/mixins'; - -.prfDevTool { - height: calc(100vh - #{$euiHeaderChildSize}); - overflow: hidden; - - .devApp__container { - overflow: hidden; - flex-shrink: 1; - } -} - -.prfDevTool__container { - display: flex; - flex: 1 1 auto; - padding: $euiSize; - overflow: hidden; - - // SASSTODO/EUITODO/HACK: Reset font styles of headers - h1, h2, h3, h4, h5, h6 { - font: inherit; - } -} - -@include euiPanel('prfDevTool__main'); - -.prfDevTool__main { - flex: 3; - order: 2; - margin-left: $euiSize; - display: flex; - overflow: hidden; - flex-direction: column; - - // Make only the tab content scroll - search-profiler-tabs { - flex-shrink: 0; - } - - profiletree { - overflow-y: auto; - flex-grow: 1; - } -} - -.prfDevTool__details { - max-width: map-get($euiBreakpoints, 's'); -} - -highlightdetails { - flex-grow: 1; - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; -} - -.prfDevTool__sense { - flex: 1; - order: 1; - position: relative; - display: flex; - flex-direction: column; - - > * { - flex-shrink: 0; - } - - .kuiTextInput { - width: 100%; - } - - // Ace Editor overrides - .ace_editor { - min-height: $euiSize * 10; - flex-grow: 1; - margin-bottom: $euiSize; - margin-top: $euiSize; - outline: solid 1px $euiColorLightShade; - } - - .errorMarker { - position: absolute; - background: rgba($euiColorDanger, .5); - z-index: 20; - } -} - -@include euiBreakpoint('xs', 's') { - .prfDevTool__container { - flex-direction: column; - } - - .prfDevTool__sense, - .prfDevTool__main { - flex: 0 0 auto; - } - - .prfDevTool__main { - margin: $euiSize 0; - } -} diff --git a/x-pack/legacy/plugins/searchprofiler/public/templates/index.html b/x-pack/legacy/plugins/searchprofiler/public/templates/index.html deleted file mode 100644 index a2874af17817e2..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/public/templates/index.html +++ /dev/null @@ -1,113 +0,0 @@ - -
    -
    - - - - - - - - -
    -

    - -

    - -

    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    -
    - -
    -
    - -
    - -
    - -
    -
    -
    - - -
    - - -
    -
    - - -
    -
    diff --git a/x-pack/legacy/plugins/searchprofiler/server/np_ready/index.ts b/x-pack/legacy/plugins/searchprofiler/server/np_ready/index.ts new file mode 100644 index 00000000000000..9253a0d6b15242 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/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. + */ +import { SearchProfilerServerPlugin } from './plugin'; + +export const plugin = () => { + return new SearchProfilerServerPlugin(); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/server/lib/__tests__/check_license.js b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.test.ts similarity index 80% rename from x-pack/legacy/plugins/searchprofiler/server/lib/__tests__/check_license.js rename to x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.test.ts index f1e19b61cbe2b0..f473533fbd48ae 100644 --- a/x-pack/legacy/plugins/searchprofiler/server/lib/__tests__/check_license.js +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.test.ts @@ -6,15 +6,14 @@ import expect from '@kbn/expect'; import { set } from 'lodash'; -import { checkLicense } from '../check_license'; +import { checkLicense } from './check_license'; -describe('check_license', function () { - - let mockLicenseInfo; - beforeEach(() => mockLicenseInfo = {}); +describe('check_license', () => { + let mockLicenseInfo: any; + beforeEach(() => (mockLicenseInfo = {})); describe('license information is not available', () => { - beforeEach(() => mockLicenseInfo.isAvailable = () => false); + beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true); @@ -37,11 +36,11 @@ describe('check_license', function () { describe('& license is active', () => { beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - it ('should set showLinks to true', () => { + it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true); }); - it ('should set enableLinks to true', () => { + it('should set enableLinks to true', () => { expect(checkLicense(mockLicenseInfo).enableAppLink).to.be(true); }); }); @@ -49,11 +48,11 @@ describe('check_license', function () { describe('& license is expired', () => { beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - it ('should set showLinks to true', () => { + it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showAppLink).to.be(true); }); - it ('should set enableLinks to false', () => { + it('should set enableLinks to false', () => { expect(checkLicense(mockLicenseInfo).enableAppLink).to.be(false); }); }); @@ -65,7 +64,7 @@ describe('check_license', function () { describe('& license is active', () => { beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - it ('should set showLinks to false', () => { + it('should set showLinks to false', () => { expect(checkLicense(mockLicenseInfo).showAppLink).to.be(false); }); }); @@ -73,7 +72,7 @@ describe('check_license', function () { describe('& license is expired', () => { beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - it ('should set showLinks to false', () => { + it('should set showLinks to false', () => { expect(checkLicense(mockLicenseInfo).showAppLink).to.be(false); }); }); diff --git a/x-pack/legacy/plugins/searchprofiler/server/lib/check_license.js b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.ts similarity index 67% rename from x-pack/legacy/plugins/searchprofiler/server/lib/check_license.js rename to x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.ts index 1093daaafe18a8..e41925da3e2688 100644 --- a/x-pack/legacy/plugins/searchprofiler/server/lib/check_license.js +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/check_license.ts @@ -5,43 +5,46 @@ */ import { i18n } from '@kbn/i18n'; +import { XPackInfo } from '../../../../xpack_main/server/lib/xpack_info'; -export function checkLicense(xpackLicenseInfo) { - +export function checkLicense( + xpackLicenseInfo: XPackInfo +): { showAppLink: boolean; enableAppLink: boolean; message: string | undefined } { if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { return { showAppLink: true, enableAppLink: false, message: i18n.translate('xpack.searchProfiler.unavailableLicenseInformationMessage', { - defaultMessage: 'Search Profiler is unavailable - license information is not available at this time.', + defaultMessage: + 'Search Profiler is unavailable - license information is not available at this time.', }), }; } const isLicenseActive = xpackLicenseInfo.license.isActive(); - let message; + let message: string | undefined; if (!isLicenseActive) { message = i18n.translate('xpack.searchProfiler.licenseHasExpiredMessage', { defaultMessage: 'Search Profiler is unavailable - license has expired.', }); } - if (xpackLicenseInfo.license.isOneOf([ 'trial', 'basic', 'standard', 'gold', 'platinum' ])) { + if (xpackLicenseInfo.license.isOneOf(['trial', 'basic', 'standard', 'gold', 'platinum'])) { return { showAppLink: true, enableAppLink: isLicenseActive, - message + message, }; } message = i18n.translate('xpack.searchProfiler.upgradeLicenseMessage', { defaultMessage: 'Search Profiler is unavailable for the current {licenseInfo} license. Please upgrade your license.', - values: { licenseInfo: xpackLicenseInfo.license.getType() } + values: { licenseInfo: xpackLicenseInfo.license.getType() }, }); return { showAppLink: false, enableAppLink: false, - message + message, }; } diff --git a/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/index.ts b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/index.ts new file mode 100644 index 00000000000000..f2c070fd44b6e6 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/lib/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 { checkLicense } from './check_license'; diff --git a/x-pack/legacy/plugins/searchprofiler/server/np_ready/plugin.ts b/x-pack/legacy/plugins/searchprofiler/server/np_ready/plugin.ts new file mode 100644 index 00000000000000..e00e2829f980d2 --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/plugin.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 } from 'src/core/server'; +import { LegacySetup } from './types'; +import { checkLicense } from './lib'; +// @ts-ignore +import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status'; + +import * as profileRoute from './routes/profile'; + +export class SearchProfilerServerPlugin implements Plugin { + async setup( + core: CoreSetup, + { + route, + plugins: { + __LEGACY: { thisPlugin, elasticsearch, xpackMain, commonRouteConfig }, + }, + }: LegacySetup + ) { + mirrorPluginStatus(xpackMain, thisPlugin); + (xpackMain as any).status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMain.info.feature(thisPlugin.id).registerLicenseCheckResultsGenerator(checkLicense); + }); + + profileRoute.register({ elasticsearch }, route, commonRouteConfig); + } + + async start() {} + + stop(): void {} +} diff --git a/x-pack/legacy/plugins/searchprofiler/server/np_ready/routes/profile.ts b/x-pack/legacy/plugins/searchprofiler/server/np_ready/routes/profile.ts new file mode 100644 index 00000000000000..082307b5a7a2be --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/routes/profile.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. + */ +import Joi from 'joi'; +import { RequestShim, ServerShim, RegisterRoute } from '../types'; + +export const handler = async (server: ServerShim, request: RequestShim) => { + const { callWithRequest } = server.elasticsearch.getCluster('data'); + let parsed = request.payload.query; + parsed.profile = true; + parsed = JSON.stringify(parsed, null, 2); + + const body = { + index: request.payload.index, + body: parsed, + }; + try { + const resp = await callWithRequest(request, 'search', body); + return { + ok: true, + resp, + }; + } catch (err) { + return { + ok: false, + err, + }; + } +}; + +export const register = (server: ServerShim, route: RegisterRoute, commonConfig: any) => { + route({ + path: '/api/searchprofiler/profile', + method: 'POST', + config: { + ...commonConfig, + validate: { + payload: Joi.object() + .keys({ + query: Joi.object().required(), + index: Joi.string().required(), + type: Joi.string().optional(), + }) + .required(), + }, + }, + handler: req => { + return handler(server, { headers: req.headers, payload: req.payload as any }); + }, + }); +}; diff --git a/x-pack/legacy/plugins/searchprofiler/server/np_ready/types.ts b/x-pack/legacy/plugins/searchprofiler/server/np_ready/types.ts new file mode 100644 index 00000000000000..7862aa386785bd --- /dev/null +++ b/x-pack/legacy/plugins/searchprofiler/server/np_ready/types.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ServerRoute } from 'hapi'; +import { ElasticsearchPlugin, Request } from 'src/legacy/core_plugins/elasticsearch'; +import { XPackMainPlugin } from '../../../xpack_main/xpack_main'; + +export type RegisterRoute = (args: ServerRoute & { config: any }) => void; + +export interface LegacyPlugins { + __LEGACY: { + thisPlugin: any; + elasticsearch: ElasticsearchPlugin; + xpackMain: XPackMainPlugin; + commonRouteConfig: any; + }; +} + +export interface LegacySetup { + route: RegisterRoute; + plugins: LegacyPlugins; +} + +export interface ServerShim { + elasticsearch: ElasticsearchPlugin; +} + +export interface RequestShim extends Request { + payload: any; +} diff --git a/x-pack/legacy/plugins/searchprofiler/server/routes/profile.js b/x-pack/legacy/plugins/searchprofiler/server/routes/profile.js deleted file mode 100644 index b1ba7527b69dd8..00000000000000 --- a/x-pack/legacy/plugins/searchprofiler/server/routes/profile.js +++ /dev/null @@ -1,51 +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 Joi from 'joi'; - -export function profileRoute(server, commonRouteConfig) { - - server.route({ - path: '/api/searchprofiler/profile', - method: 'POST', - config: { - ...commonRouteConfig, - validate: { - payload: Joi.object().keys({ - query: Joi.object().required(), - index: Joi.string().required(), - type: Joi.string().optional() - }).required() //Joi validation - } - }, - handler: async (request) => { - - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); - let parsed = request.payload.query; - parsed.profile = true; - parsed = JSON.stringify(parsed, null, 2); - - const body = { - index: request.payload.index, - type: request.payload.type, - body: parsed - }; - try { - const resp = await callWithRequest(request, 'search', body); - return { - ok: true, - resp: resp - }; - } catch (err) { - return { - ok: false, - err: err - }; - } - } - }); - -} diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index 1c712f874969c2..b9c28105e99d15 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -5,7 +5,6 @@ */ import { EuiLink, EuiText } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; import { createPortalNode, InPortal } from 'react-reverse-portal'; import { Query } from 'src/plugins/data/common'; @@ -30,6 +29,7 @@ import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; import * as i18n from './translations'; import { MapEmbeddable, SetQuery } from './types'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; interface EmbeddableMapProps { maintainRatio?: boolean; @@ -75,7 +75,7 @@ EmbeddableMap.displayName = 'EmbeddableMap'; export interface EmbeddedMapProps { query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; startDate: number; endDate: number; setQuery: SetQuery; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 7f514b0e53b71c..a50cfa98fc977a 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import uuid from 'uuid'; import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; @@ -28,6 +27,7 @@ import { getLayerList } from './map_config'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; import * as i18n from './translations'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; /** * Displays an error toast for the provided title and message @@ -86,7 +86,7 @@ export const setupEmbeddablesAPI = (plugins: PluginsStart) => { * @throws Error if EmbeddableFactory does not exist */ export const createEmbeddable = async ( - filters: Filter[], + filters: esFilters.Filter[], indexPatterns: IndexPatternMapping[], query: Query, startDate: number, diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index b3d930a13ca355..10412ecdb50131 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter as ESFilterType } from '@kbn/es-query'; import { Query } from 'src/plugins/data/common'; import { TimeRange } from 'src/plugins/data/public'; import { @@ -14,9 +13,10 @@ import { EmbeddableFactory, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { inputsModel } from '../../store/inputs'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; export interface MapEmbeddableInput extends EmbeddableInput { - filters: ESFilterType[]; + filters: esFilters.Filter[]; query: Query; refreshConfig: { isPaused: boolean; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 13f0666b31212c..6b79a6402586e8 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -5,7 +5,7 @@ */ import { EuiPanel } from '@elastic/eui'; -import { Filter, getEsQueryConfig } from '@kbn/es-query'; +import { getEsQueryConfig } from '@kbn/es-query'; import { getOr, isEmpty, isEqual } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; @@ -31,6 +31,7 @@ import { TimelineRefetch } from '../timeline/refetch_timeline'; import { isCompactFooter } from '../timeline/timeline'; import { ManageTimelineContext } from '../timeline/timeline_context'; import * as i18n from './translations'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 500; @@ -44,7 +45,7 @@ interface Props { columns: ColumnHeader[]; dataProviders: DataProvider[]; end: number; - filters: Filter[]; + filters: esFilters.Filter[]; height?: number; id: string; indexPattern: StaticIndexPattern; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx index 9483c60dcc5525..5681588cb44b71 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { isEqual } from 'lodash/fp'; import React, { useEffect, useState, useCallback } from 'react'; import { connect } from 'react-redux'; @@ -19,6 +18,7 @@ import { ColumnHeader } from '../timeline/body/column_headers/column_header'; import { DataProvider } from '../timeline/data_providers/data_provider'; import { Sort } from '../timeline/body/sort'; import { OnChangeItemsPerPage } from '../timeline/events'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; import { EventsViewer } from './events_viewer'; import { InputsModelId } from '../../store/inputs/constants'; @@ -33,7 +33,7 @@ interface StateReduxProps { activePage?: number; columns: ColumnHeader[]; dataProviders?: DataProvider[]; - filters: Filter[]; + filters: esFilters.Filter[]; isLive: boolean; itemsPerPage?: number; itemsPerPageOptions?: number[]; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts index 68aa115c965d6d..0c44b8d44c3177 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { isEmpty } from 'lodash/fp'; import { Location } from 'history'; import { Query } from 'src/plugins/data/common'; @@ -17,6 +16,7 @@ import { replaceStateKeyInQueryString, getQueryStringFromLocation, } from '../url_state/helpers'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; import { TabNavigationProps } from './tab_navigation/types'; import { SearchNavTab } from './types'; @@ -25,7 +25,7 @@ export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): stri if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) { return URL_STATE_KEYS[tab.urlKey].reduce( (myLocation: Location, urlKey: KeyUrlState) => { - let urlStateToReplace: UrlInputsModel | Query | Filter[] | Timeline | string = ''; + let urlStateToReplace: UrlInputsModel | Query | esFilters.Filter[] | Timeline | string = ''; if (urlKey === CONSTANTS.appQuery && urlState.query != null) { if (urlState.query.query === '') { diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts index 1d5ebf2097974f..856651e6f97dc1 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; -import { Query } from 'src/plugins/data/common'; import { UrlInputsModel } from '../../../store/inputs/model'; import { CONSTANTS } from '../../url_state/constants'; import { Timeline } from '../../url_state/types'; import { HostsTableType } from '../../../store/hosts/model'; +import { esFilters, Query } from '../../../../../../../../src/plugins/data/public'; import { SiemNavigationComponentProps } from '../types'; @@ -18,7 +17,7 @@ export interface TabNavigationProps extends SiemNavigationComponentProps { pageName: string; tabName: HostsTableType | undefined; [CONSTANTS.appQuery]?: Query; - [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.filters]?: esFilters.Filter[]; [CONSTANTS.savedQuery]?: string; [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: Timeline; diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx index 34e2bc01a4ea78..ed614d79174f02 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { mount, shallow } from 'enzyme'; import toJson from 'enzyme-to-json'; import * as React from 'react'; @@ -13,9 +12,10 @@ import { apolloClientObservable, mockGlobalState, TestProviders } from '../../.. import { createStore, State } from '../../../store'; import { siemFilterManager } from '../../search_bar'; import { AddFilterToGlobalSearchBar } from '.'; +import { esFilters } from '../../../../../../../../src/plugins/data/public'; interface MockSiemFilterManager { - addFilters: (filters: Filter[]) => void; + addFilters: (filters: esFilters.Filter[]) => void; } const mockSiemFilterManager: MockSiemFilterManager = siemFilterManager as MockSiemFilterManager; jest.mock('../../search_bar', () => ({ diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx index 6f3b56417173c7..6e1e6545e5534c 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx @@ -5,12 +5,12 @@ */ import { EuiIcon, EuiPanel, EuiToolTip } from '@elastic/eui'; -import { Filter } from '@kbn/es-query'; import React from 'react'; import styled from 'styled-components'; import { WithHoverActions } from '../../with_hover_actions'; import { siemFilterManager } from '../../search_bar'; +import { esFilters } from '../../../../../../../../src/plugins/data/public'; import * as i18n from './translations'; @@ -18,7 +18,7 @@ export * from './helpers'; interface OwnProps { children: JSX.Element; - filter: Filter; + filter: esFilters.Filter; onFilterAdded?: () => void; } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/index.tsx index 40460f81fa83c6..1f502635a8de4c 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/index.tsx @@ -9,3 +9,4 @@ export { KpiNetworkComponent } from './kpi_network'; export { NetworkDnsTable } from './network_dns_table'; export { NetworkTopCountriesTable } from './network_top_countries_table'; export { NetworkTopNFlowTable } from './network_top_n_flow_table'; +export { NetworkHttpTable } from './network_http_table'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000000..dfc1b2cf64e389 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NetworkHttp Table Component rendering it renders the default NetworkHttp table 1`] = ` + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/columns.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/columns.tsx new file mode 100644 index 00000000000000..6a47a58c85f319 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/columns.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import numeral from '@elastic/numeral'; +import { NetworkHttpEdges, NetworkHttpFields, NetworkHttpItem } from '../../../../graphql/types'; +import { escapeDataProviderId } from '../../../drag_and_drop/helpers'; +import { getEmptyTagValue } from '../../../empty_value'; +import { IPDetailsLink } from '../../../links'; +import { Columns } from '../../../paginated_table'; + +import * as i18n from './translations'; +import { getRowItemDraggable, getRowItemDraggables } from '../../../tables/helpers'; +export type NetworkHttpColumns = [ + Columns, + Columns, + Columns, + Columns, + Columns, + Columns, + Columns +]; + +export const getNetworkHttpColumns = (tableId: string): NetworkHttpColumns => [ + { + name: i18n.METHOD, + render: ({ node: { methods, path } }) => { + return Array.isArray(methods) && methods.length > 0 + ? getRowItemDraggables({ + attrName: 'http.request.method', + displayCount: 3, + idPrefix: escapeDataProviderId(`${tableId}-table-methods-${path}`), + rowItems: methods, + }) + : getEmptyTagValue(); + }, + }, + { + name: i18n.DOMAIN, + render: ({ node: { domains, path } }) => + Array.isArray(domains) && domains.length > 0 + ? getRowItemDraggables({ + attrName: 'url.domain', + displayCount: 3, + idPrefix: escapeDataProviderId(`${tableId}-table-domains-${path}`), + rowItems: domains, + }) + : getEmptyTagValue(), + }, + { + field: `node.${NetworkHttpFields.path}`, + name: i18n.PATH, + render: path => + path != null + ? getRowItemDraggable({ + attrName: 'url.path', + idPrefix: escapeDataProviderId(`${tableId}-table-path-${path}`), + rowItem: path, + }) + : getEmptyTagValue(), + }, + { + name: i18n.STATUS, + render: ({ node: { statuses, path } }) => + Array.isArray(statuses) && statuses.length > 0 + ? getRowItemDraggables({ + attrName: 'http.response.status_code', + displayCount: 3, + idPrefix: escapeDataProviderId(`${tableId}-table-statuses-${path}`), + rowItems: statuses, + }) + : getEmptyTagValue(), + }, + { + name: i18n.LAST_HOST, + render: ({ node: { lastHost, path } }) => + lastHost != null + ? getRowItemDraggable({ + attrName: 'host.name', + idPrefix: escapeDataProviderId(`${tableId}-table-lastHost-${path}`), + rowItem: lastHost, + }) + : getEmptyTagValue(), + }, + { + name: i18n.LAST_SOURCE_IP, + render: ({ node: { lastSourceIp, path } }) => + lastSourceIp != null + ? getRowItemDraggable({ + attrName: 'source.ip', + idPrefix: escapeDataProviderId(`${tableId}-table-lastSourceIp-${path}`), + rowItem: lastSourceIp, + render: () => , + }) + : getEmptyTagValue(), + }, + { + align: 'right', + field: `node.${NetworkHttpFields.requestCount}`, + name: i18n.REQUESTS, + sortable: true, + render: requestCount => { + if (requestCount != null) { + return numeral(requestCount).format('0,000'); + } else { + return getEmptyTagValue(); + } + }, + }, +]; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.test.tsx new file mode 100644 index 00000000000000..277e136d776fa6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.test.tsx @@ -0,0 +1,104 @@ +/* + * 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 { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import { getOr } from 'lodash/fp'; +import * as React from 'react'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { Provider as ReduxStoreProvider } from 'react-redux'; + +import { apolloClientObservable, mockGlobalState, TestProviders } from '../../../../mock'; +import { createStore, networkModel, State } from '../../../../store'; + +import { NetworkHttpTable } from '.'; +import { mockData } from './mock'; + +jest.mock('../../../../lib/settings/use_kibana_ui_setting'); + +describe('NetworkHttp Table Component', () => { + const loadPage = jest.fn(); + const state: State = mockGlobalState; + + let store = createStore(state, apolloClientObservable); + + beforeEach(() => { + store = createStore(state, apolloClientObservable); + }); + + describe('rendering', () => { + test('it renders the default NetworkHttp table', () => { + const wrapper = shallow( + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + }); + + describe('Sorting', () => { + test('when you click on the column header, you should show the sorting icon', () => { + const wrapper = mount( + + + + + + ); + + expect(store.getState().network.page.queries!.http.sort).toEqual({ + direction: 'desc', + }); + + wrapper + .find('.euiTable thead tr th button') + .first() + .simulate('click'); + + wrapper.update(); + + expect(store.getState().network.page.queries!.http.sort).toEqual({ + direction: 'asc', + }); + expect( + wrapper + .find('.euiTable thead tr th button') + .first() + .find('svg') + ).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx new file mode 100644 index 00000000000000..71807280ebcb45 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { ActionCreator } from 'typescript-fsa'; + +import { networkActions } from '../../../../store/actions'; +import { + NetworkHttpEdges, + NetworkHttpFields, + NetworkHttpSortField, +} from '../../../../graphql/types'; +import { networkModel, networkSelectors, State } from '../../../../store'; +import { Criteria, ItemsPerRow, PaginatedTable } from '../../../paginated_table'; + +import { getNetworkHttpColumns } from './columns'; +import * as i18n from './translations'; + +interface OwnProps { + data: NetworkHttpEdges[]; + fakeTotalCount: number; + id: string; + isInspect: boolean; + loading: boolean; + loadPage: (newActivePage: number) => void; + showMorePagesIndicator: boolean; + totalCount: number; + type: networkModel.NetworkType; +} + +interface NetworkHttpTableReduxProps { + activePage: number; + limit: number; + sort: NetworkHttpSortField; +} + +interface NetworkHttpTableDispatchProps { + updateNetworkTable: ActionCreator<{ + networkType: networkModel.NetworkType; + tableType: networkModel.AllNetworkTables; + updates: networkModel.TableUpdates; + }>; +} + +type NetworkHttpTableProps = OwnProps & NetworkHttpTableReduxProps & NetworkHttpTableDispatchProps; + +const rowItems: ItemsPerRow[] = [ + { + text: i18n.ROWS_5, + numberOfRow: 5, + }, + { + text: i18n.ROWS_10, + numberOfRow: 10, + }, +]; + +const NetworkHttpTableComponent = React.memo( + ({ + activePage, + data, + fakeTotalCount, + id, + isInspect, + limit, + loading, + loadPage, + showMorePagesIndicator, + sort, + totalCount, + type, + updateNetworkTable, + }) => { + const onChange = (criteria: Criteria, tableType: networkModel.HttpTableType) => { + if (criteria.sort != null && criteria.sort.direction !== sort.direction) { + updateNetworkTable({ + networkType: type, + tableType, + updates: { + sort: { + direction: criteria.sort.direction, + }, + }, + }); + } + }; + const tableType = + type === networkModel.NetworkType.page + ? networkModel.NetworkTableType.http + : networkModel.IpDetailsTableType.http; + return ( + loadPage(newActivePage)} + onChange={criteria => onChange(criteria, tableType)} + pageOfItems={data} + showMorePagesIndicator={showMorePagesIndicator} + sorting={{ field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }} + totalCount={fakeTotalCount} + updateActivePage={newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }) + } + updateLimitPagination={newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }) + } + /> + ); + } +); + +NetworkHttpTableComponent.displayName = 'NetworkHttpTableComponent'; + +const makeMapStateToProps = () => { + const getNetworkHttpSelector = networkSelectors.httpSelector(); + const mapStateToProps = (state: State, { type }: OwnProps) => getNetworkHttpSelector(state, type); + return mapStateToProps; +}; + +export const NetworkHttpTable = compose>( + connect( + makeMapStateToProps, + { + updateNetworkTable: networkActions.updateNetworkTable, + } + ) +)(NetworkHttpTableComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/mock.ts b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/mock.ts new file mode 100644 index 00000000000000..ed9b00ba8e49e5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/mock.ts @@ -0,0 +1,88 @@ +/* + * 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 { NetworkHttpData } from '../../../../graphql/types'; + +export const mockData: { NetworkHttp: NetworkHttpData } = { + NetworkHttp: { + edges: [ + { + node: { + _id: '/computeMetadata/v1/instance/virtual-clock/drift-token', + domains: ['metadata.google.internal'], + methods: ['get'], + statuses: [], + lastHost: 'suricata-iowa', + lastSourceIp: '10.128.0.21', + path: '/computeMetadata/v1/instance/virtual-clock/drift-token', + requestCount: 1440, + }, + cursor: { + value: '/computeMetadata/v1/instance/virtual-clock/drift-token', + tiebreaker: null, + }, + }, + { + node: { + _id: '/computeMetadata/v1/', + domains: ['metadata.google.internal'], + methods: ['get'], + statuses: ['200'], + lastHost: 'suricata-iowa', + lastSourceIp: '10.128.0.21', + path: '/computeMetadata/v1/', + requestCount: 1020, + }, + cursor: { + value: '/computeMetadata/v1/', + tiebreaker: null, + }, + }, + { + node: { + _id: '/computeMetadata/v1/instance/network-interfaces/', + domains: ['metadata.google.internal'], + methods: ['get'], + statuses: [], + lastHost: 'suricata-iowa', + lastSourceIp: '10.128.0.21', + path: '/computeMetadata/v1/instance/network-interfaces/', + requestCount: 960, + }, + cursor: { + value: '/computeMetadata/v1/instance/network-interfaces/', + tiebreaker: null, + }, + }, + { + node: { + _id: '/downloads/ca_setup.exe', + domains: ['www.oxid.it'], + methods: ['get'], + statuses: ['200'], + lastHost: 'jessie', + lastSourceIp: '10.0.2.15', + path: '/downloads/ca_setup.exe', + requestCount: 3, + }, + cursor: { + value: '/downloads/ca_setup.exe', + tiebreaker: null, + }, + }, + ], + inspect: { + dsl: [''], + response: [''], + }, + pageInfo: { + activePage: 0, + fakeTotalCount: 4, + showMorePagesIndicator: false, + }, + totalCount: 4, + }, +}; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/translations.ts b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/translations.ts new file mode 100644 index 00000000000000..c891a7c75fc3c1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/translations.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const HTTP_REQUESTS = i18n.translate('xpack.siem.networkHttpTable.title', { + defaultMessage: 'HTTP Requests', +}); + +export const UNIT = (totalCount: number) => + i18n.translate('xpack.siem.networkHttpTable.unit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {request} other {requests}}`, + }); + +export const METHOD = i18n.translate('xpack.siem.networkHttpTable.column.methodTitle', { + defaultMessage: 'Method', +}); +export const DOMAIN = i18n.translate('xpack.siem.networkHttpTable.column.domainTitle', { + defaultMessage: 'Domain', +}); + +export const PATH = i18n.translate('xpack.siem.networkHttpTable.column.pathTitle', { + defaultMessage: 'Path', +}); + +export const STATUS = i18n.translate('xpack.siem.networkHttpTable.column.statusTitle', { + defaultMessage: 'Status', +}); + +export const LAST_HOST = i18n.translate('xpack.siem.networkHttpTable.column.lastHostTitle', { + defaultMessage: 'Last host', +}); + +export const LAST_SOURCE_IP = i18n.translate( + 'xpack.siem.networkHttpTable.column.lastSourceIpTitle', + { + defaultMessage: 'Last source Ip', + } +); + +export const REQUESTS = i18n.translate('xpack.siem.networkHttpTable.column.requestsTitle', { + defaultMessage: 'Requests', +}); + +export const ROWS_5 = i18n.translate('xpack.siem.networkHttpTable.rows', { + values: { numRows: 5 }, + defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', +}); + +export const ROWS_10 = i18n.translate('xpack.siem.networkHttpTable.rows', { + values: { numRows: 10 }, + defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', +}); diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx index 4de64c6b32aa96..646d003051e836 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx @@ -23,6 +23,7 @@ import { Direction } from '../../graphql/types'; import { AuthTableColumns } from '../page/hosts/authentications_table'; import { HostsTableColumns } from '../page/hosts/hosts_table'; import { NetworkDnsColumns } from '../page/network/network_dns_table/columns'; +import { NetworkHttpColumns } from '../page/network/network_http_table/columns'; import { NetworkTopNFlowColumns, NetworkTopNFlowColumnsIpDetails, @@ -72,6 +73,7 @@ declare type BasicTableColumns = | HostsTableColumns | HostsTableColumnsTest | NetworkDnsColumns + | NetworkHttpColumns | NetworkTopCountriesColumns | NetworkTopCountriesColumnsIpDetails | NetworkTopNFlowColumns diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index e024a4e68492b3..c885d001542e58 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { getOr, isEqual, set } from 'lodash/fp'; import React, { memo, useEffect, useCallback, useMemo } from 'react'; import { connect } from 'react-redux'; @@ -37,6 +36,7 @@ import { toStrSelector, } from './selectors'; import { timelineActions, hostsActions, networkActions } from '../../store/actions'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; const { ui: { SearchBar }, @@ -67,7 +67,7 @@ interface SiemSearchBarDispatch { id: InputsModelId; savedQuery: SavedQuery | undefined; }) => void; - setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => void; + setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: esFilters.Filter[] }) => void; } interface SiemSearchBarProps { @@ -313,7 +313,7 @@ SearchBarComponent.displayName = 'SiemSearchBar'; interface UpdateReduxSearchBar extends OnTimeChangeProps { id: InputsModelId; - filters?: Filter[]; + filters?: esFilters.Filter[]; query?: Query; savedQuery?: SavedQuery; resetSavedQuery?: boolean; @@ -397,7 +397,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateSearch: dispatchUpdateSearch(dispatch), setSavedQuery: ({ id, savedQuery }: { id: InputsModelId; savedQuery: SavedQuery | undefined }) => dispatch(inputsActions.setSavedQuery({ id, savedQuery })), - setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => + setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: esFilters.Filter[] }) => dispatch(inputsActions.setSearchBarFilter({ id, filters })), }); diff --git a/x-pack/legacy/plugins/siem/public/components/tables/__snapshots__/helpers.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/tables/__snapshots__/helpers.test.tsx.snap index aa8666c247caef..1e6c5eb89352bd 100644 --- a/x-pack/legacy/plugins/siem/public/components/tables/__snapshots__/helpers.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/tables/__snapshots__/helpers.test.tsx.snap @@ -33,7 +33,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh "and": Array [], "enabled": true, "excluded": false, - "id": "idPrefix-attrName-item1", + "id": "idPrefix-attrName-item1-0", "kqlQuery": "", "name": "item1", "queryMatch": Object { @@ -44,7 +44,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh }, } } - key="idPrefix-attrName-item1" + key="idPrefix-attrName-item1-0" render={[Function]} /> , @@ -55,7 +55,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh "and": Array [], "enabled": true, "excluded": false, - "id": "idPrefix-attrName-item2", + "id": "idPrefix-attrName-item2-1", "kqlQuery": "", "name": "item2", "queryMatch": Object { @@ -66,7 +66,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh }, } } - key="idPrefix-attrName-item2" + key="idPrefix-attrName-item2-1" render={[Function]} /> , @@ -77,7 +77,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh "and": Array [], "enabled": true, "excluded": false, - "id": "idPrefix-attrName-item3", + "id": "idPrefix-attrName-item3-2", "kqlQuery": "", "name": "item3", "queryMatch": Object { @@ -88,7 +88,7 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh }, } } - key="idPrefix-attrName-item3" + key="idPrefix-attrName-item3-2" render={[Function]} /> diff --git a/x-pack/legacy/plugins/siem/public/components/tables/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/tables/helpers.tsx index 2557630a603061..b4ee93f9963e43 100644 --- a/x-pack/legacy/plugins/siem/public/components/tables/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/tables/helpers.tsx @@ -89,7 +89,7 @@ export const getRowItemDraggables = ({ }): JSX.Element => { if (rowItems != null && rowItems.length > 0) { const draggables = rowItems.slice(0, displayCount).map((rowItem, index) => { - const id = escapeDataProviderId(`${idPrefix}-${attrName}-${rowItem}`); + const id = escapeDataProviderId(`${idPrefix}-${attrName}-${rowItem}-${index}`); return ( {index !== 0 && ( diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx index a18e4d7962e955..6182fca6e2e993 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { isEmpty, isNumber, get } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { escapeQueryValue, convertToBuildEsQuery, EsQueryConfig } from '../../lib/keury'; @@ -106,7 +105,7 @@ export const combineQueries = ({ dataProviders: DataProvider[]; indexPattern: StaticIndexPattern; browserFields: BrowserFields; - filters: Filter[]; + filters: esFilters.Filter[]; kqlQuery: Query; kqlMode: string; start: number; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 9c49d356cd4bca..f7487d7a81a7a3 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { decode, encode, RisonValue } from 'rison-node'; import { Location } from 'history'; import { QueryString } from 'ui/utils/query_string'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { inputsSelectors, State, timelineSelectors } from '../../store'; import { SiemPageName } from '../../pages/home/types'; @@ -154,7 +153,7 @@ export const makeMapStateToProps = () => { let searchAttr: { [CONSTANTS.appQuery]?: Query; - [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.filters]?: esFilters.Filter[]; [CONSTANTS.savedQuery]?: string; } = { [CONSTANTS.appQuery]: getGlobalQuerySelector(state), diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx index fa3b2788667042..013983c78a3a51 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { get, isEmpty } from 'lodash/fp'; import { Dispatch } from 'redux'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { inputsActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; @@ -125,7 +124,7 @@ export const dispatchSetInitialStateFromUrl = ( } if (urlKey === CONSTANTS.filters) { - const filters: Filter[] = decodeRisonUrlState(newUrlStateString); + const filters: esFilters.Filter[] = decodeRisonUrlState(newUrlStateString); siemFilterManager.setFilters(filters || []); } diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts index f858ffc32ddbcb..44c050a1990ce3 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import ApolloClient from 'apollo-client'; import { ActionCreator } from 'typescript-fsa'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { UrlInputsModel } from '../../store/inputs/model'; import { RouteSpyState } from '../../utils/route/types'; @@ -60,7 +59,7 @@ export interface Timeline { export interface UrlState { [CONSTANTS.appQuery]?: Query; - [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.filters]?: esFilters.Filter[]; [CONSTANTS.savedQuery]?: string; [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: Timeline; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx index 5b9511f169744e..f1eeb4e6fbec40 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx @@ -3,11 +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 { Filter } from '@kbn/es-query'; + import { Location } from 'history'; import { isEqual, difference, isEmpty } from 'lodash/fp'; import { useEffect, useRef, useState } from 'react'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { UrlInputsModel } from '../../store/inputs/model'; import { useApolloClient } from '../../utils/apollo_context'; @@ -59,7 +59,7 @@ export const useUrlStateHooks = ({ const prevProps = usePrevious({ pathName, urlState }); const replaceStateInLocation = ( - urlStateToReplace: UrlInputsModel | Query | Filter[] | Timeline | string, + urlStateToReplace: UrlInputsModel | Query | esFilters.Filter[] | Timeline | string, urlStateKey: string, latestLocation: Location = { hash: '', @@ -94,7 +94,10 @@ export const useUrlStateHooks = ({ urlKey ); if (newUrlStateString) { - const queryState: Query | Timeline | Filter[] = decodeRisonUrlState(newUrlStateString); + const queryState: Query | Timeline | esFilters.Filter[] = decodeRisonUrlState( + newUrlStateString + ); + if ( urlKey === CONSTANTS.appQuery && queryState != null && diff --git a/x-pack/legacy/plugins/siem/public/containers/network_http/index.gql_query.ts b/x-pack/legacy/plugins/siem/public/containers/network_http/index.gql_query.ts new file mode 100644 index 00000000000000..bedf13dfa98495 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/network_http/index.gql_query.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import gql from 'graphql-tag'; + +export const networkHttpQuery = gql` + query GetNetworkHttpQuery( + $sourceId: ID! + $ip: String + $filterQuery: String + $pagination: PaginationInputPaginated! + $sort: NetworkHttpSortField! + $timerange: TimerangeInput! + $defaultIndex: [String!]! + $inspect: Boolean! + ) { + source(id: $sourceId) { + id + NetworkHttp( + filterQuery: $filterQuery + ip: $ip + pagination: $pagination + sort: $sort + timerange: $timerange + defaultIndex: $defaultIndex + ) { + totalCount + edges { + node { + domains + lastHost + lastSourceIp + methods + path + requestCount + statuses + } + cursor { + value + } + } + pageInfo { + activePage + fakeTotalCount + showMorePagesIndicator + } + inspect @include(if: $inspect) { + dsl + response + } + } + } + } +`; diff --git a/x-pack/legacy/plugins/siem/public/containers/network_http/index.tsx b/x-pack/legacy/plugins/siem/public/containers/network_http/index.tsx new file mode 100644 index 00000000000000..d76ab53b2de3a9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/network_http/index.tsx @@ -0,0 +1,154 @@ +/* + * 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 { getOr } from 'lodash/fp'; +import React from 'react'; +import { Query } from 'react-apollo'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import chrome from 'ui/chrome'; + +import { DEFAULT_INDEX_KEY } from '../../../common/constants'; +import { + GetNetworkHttpQuery, + NetworkHttpEdges, + NetworkHttpSortField, + PageInfoPaginated, +} from '../../graphql/types'; +import { inputsModel, inputsSelectors, networkModel, networkSelectors, State } from '../../store'; +import { generateTablePaginationOptions } from '../../components/paginated_table/helpers'; +import { createFilter, getDefaultFetchPolicy } from '../helpers'; +import { QueryTemplatePaginated, QueryTemplatePaginatedProps } from '../query_template_paginated'; +import { networkHttpQuery } from './index.gql_query'; + +const ID = 'networkHttpQuery'; + +export interface NetworkHttpArgs { + id: string; + ip?: string; + inspect: inputsModel.InspectQuery; + isInspected: boolean; + loading: boolean; + loadPage: (newActivePage: number) => void; + networkHttp: NetworkHttpEdges[]; + pageInfo: PageInfoPaginated; + refetch: inputsModel.Refetch; + totalCount: number; +} + +export interface OwnProps extends QueryTemplatePaginatedProps { + children: (args: NetworkHttpArgs) => React.ReactNode; + ip?: string; + type: networkModel.NetworkType; +} + +export interface NetworkHttpComponentReduxProps { + activePage: number; + isInspected: boolean; + limit: number; + sort: NetworkHttpSortField; +} + +type NetworkHttpProps = OwnProps & NetworkHttpComponentReduxProps; + +class NetworkHttpComponentQuery extends QueryTemplatePaginated< + NetworkHttpProps, + GetNetworkHttpQuery.Query, + GetNetworkHttpQuery.Variables +> { + public render() { + const { + activePage, + children, + endDate, + filterQuery, + id = ID, + ip, + isInspected, + limit, + skip, + sourceId, + sort, + startDate, + } = this.props; + const variables: GetNetworkHttpQuery.Variables = { + defaultIndex: chrome.getUiSettingsClient().get(DEFAULT_INDEX_KEY), + filterQuery: createFilter(filterQuery), + inspect: isInspected, + ip, + pagination: generateTablePaginationOptions(activePage, limit), + sort, + sourceId, + timerange: { + interval: '12h', + from: startDate!, + to: endDate!, + }, + }; + return ( + + fetchPolicy={getDefaultFetchPolicy()} + notifyOnNetworkStatusChange + query={networkHttpQuery} + skip={skip} + variables={variables} + > + {({ data, loading, fetchMore, networkStatus, refetch }) => { + const networkHttp = getOr([], `source.NetworkHttp.edges`, data); + this.setFetchMore(fetchMore); + this.setFetchMoreOptions((newActivePage: number) => ({ + variables: { + pagination: generateTablePaginationOptions(newActivePage, limit), + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) { + return prev; + } + return { + ...fetchMoreResult, + source: { + ...fetchMoreResult.source, + NetworkHttp: { + ...fetchMoreResult.source.NetworkHttp, + edges: [...fetchMoreResult.source.NetworkHttp.edges], + }, + }, + }; + }, + })); + const isLoading = this.isItAValidLoading(loading, variables, networkStatus); + return children({ + id, + inspect: getOr(null, 'source.NetworkHttp.inspect', data), + isInspected, + loading: isLoading, + loadPage: this.wrappedLoadMore, + networkHttp, + pageInfo: getOr({}, 'source.NetworkHttp.pageInfo', data), + refetch: this.memoizedRefetchQuery(variables, limit, refetch), + totalCount: getOr(-1, 'source.NetworkHttp.totalCount', data), + }); + }} + + ); + } +} + +const makeMapStateToProps = () => { + const getHttpSelector = networkSelectors.httpSelector(); + const getQuery = inputsSelectors.globalQueryByIdSelector(); + return (state: State, { id = ID, type }: OwnProps) => { + const { isInspected } = getQuery(state, id); + return { + ...getHttpSelector(state, type), + isInspected, + }; + }; +}; + +export const NetworkHttpQuery = compose>( + connect(makeMapStateToProps) +)(NetworkHttpComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index 488a6310db3f27..8732b61e5c0eb0 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -1777,6 +1777,93 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "NetworkHttp", + "description": "", + "args": [ + { + "name": "id", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "filterQuery", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "ip", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "pagination", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "PaginationInputPaginated", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "sort", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "NetworkHttpSortField", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "timerange", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "defaultIndex", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NetworkHttpData", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "OverviewNetwork", "description": "", @@ -8048,6 +8135,236 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "NetworkHttpSortField", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "direction", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "NetworkHttpData", + "description": "", + "fields": [ + { + "name": "edges", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NetworkHttpEdges", "ofType": null } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "totalCount", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "inspect", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "NetworkHttpEdges", + "description": "", + "fields": [ + { + "name": "node", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NetworkHttpItem", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "cursor", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "NetworkHttpItem", + "description": "", + "fields": [ + { + "name": "_id", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "domains", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastHost", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSourceIp", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "methods", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "path", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "requestCount", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "statuses", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "OverviewNetworkData", @@ -11275,6 +11592,54 @@ ], "possibleTypes": null }, + { + "kind": "ENUM", + "name": "NetworkHttpFields", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "domains", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastHost", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSourceIp", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "methods", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "path", "description": "", "isDeprecated": false, "deprecationReason": null }, + { + "name": "requestCount", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "statuses", + "description": "", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "ENUM", "name": "FlowDirection", diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index dbb62d73e5994a..a1b259b876a142 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -85,6 +85,10 @@ export interface NetworkDnsSortField { direction: Direction; } +export interface NetworkHttpSortField { + direction: Direction; +} + export interface TlsSortField { field: TlsFields; @@ -294,6 +298,16 @@ export enum NetworkDirectionEcs { unknown = 'unknown', } +export enum NetworkHttpFields { + domains = 'domains', + lastHost = 'lastHost', + lastSourceIp = 'lastSourceIp', + methods = 'methods', + path = 'path', + requestCount = 'requestCount', + statuses = 'statuses', +} + export enum FlowDirection { uniDirectional = 'uniDirectional', biDirectional = 'biDirectional', @@ -433,6 +447,8 @@ export interface Source { NetworkDns: NetworkDnsData; + NetworkHttp: NetworkHttpData; + OverviewNetwork?: Maybe; OverviewHost?: Maybe; @@ -1588,6 +1604,40 @@ export interface NetworkDnsItem { uniqueDomains?: Maybe; } +export interface NetworkHttpData { + edges: NetworkHttpEdges[]; + + totalCount: number; + + pageInfo: PageInfoPaginated; + + inspect?: Maybe; +} + +export interface NetworkHttpEdges { + node: NetworkHttpItem; + + cursor: CursorType; +} + +export interface NetworkHttpItem { + _id?: Maybe; + + domains: string[]; + + lastHost?: Maybe; + + lastSourceIp?: Maybe; + + methods: string[]; + + path?: Maybe; + + requestCount?: Maybe; + + statuses: string[]; +} + export interface OverviewNetworkData { auditbeatSocket?: Maybe; @@ -2162,6 +2212,21 @@ export interface NetworkDnsSourceArgs { defaultIndex: string[]; } +export interface NetworkHttpSourceArgs { + id?: Maybe; + + filterQuery?: Maybe; + + ip?: Maybe; + + pagination: PaginationInputPaginated; + + sort: NetworkHttpSortField; + + timerange: TimerangeInput; + + defaultIndex: string[]; +} export interface OverviewNetworkSourceArgs { id?: Maybe; @@ -3209,6 +3274,95 @@ export namespace GetNetworkDnsQuery { }; } +export namespace GetNetworkHttpQuery { + export type Variables = { + sourceId: string; + ip?: Maybe; + filterQuery?: Maybe; + pagination: PaginationInputPaginated; + sort: NetworkHttpSortField; + timerange: TimerangeInput; + defaultIndex: string[]; + inspect: boolean; + }; + + export type Query = { + __typename?: 'Query'; + + source: Source; + }; + + export type Source = { + __typename?: 'Source'; + + id: string; + + NetworkHttp: NetworkHttp; + }; + + export type NetworkHttp = { + __typename?: 'NetworkHttpData'; + + totalCount: number; + + edges: Edges[]; + + pageInfo: PageInfo; + + inspect: Maybe; + }; + + export type Edges = { + __typename?: 'NetworkHttpEdges'; + + node: Node; + + cursor: Cursor; + }; + + export type Node = { + __typename?: 'NetworkHttpItem'; + + domains: string[]; + + lastHost: Maybe; + + lastSourceIp: Maybe; + + methods: string[]; + + path: Maybe; + + requestCount: Maybe; + + statuses: string[]; + }; + + 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 GetNetworkTopCountriesQuery { export type Variables = { sourceId: string; diff --git a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts index 7bd8560a1770a7..bf8726d5ed377f 100644 --- a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts +++ b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildEsQuery, Filter, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { isEmpty, isString, flow } from 'lodash/fp'; import { StaticIndexPattern } from 'ui/index_patterns'; import { Query } from 'src/plugins/data/common'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; import { KueryFilterQuery } from '../../store'; @@ -83,7 +84,7 @@ export const convertToBuildEsQuery = ({ config: EsQueryConfig; indexPattern: StaticIndexPattern; queries: Query[]; - filters: Filter[]; + filters: esFilters.Filter[]; }) => { try { return JSON.stringify( diff --git a/x-pack/legacy/plugins/siem/public/mock/global_state.ts b/x-pack/legacy/plugins/siem/public/mock/global_state.ts index 9be209321085f4..ada34acbc19460 100644 --- a/x-pack/legacy/plugins/siem/public/mock/global_state.ts +++ b/x-pack/legacy/plugins/siem/public/mock/global_state.ts @@ -96,6 +96,11 @@ export const mockGlobalState: State = { limit: 10, sort: { field: TlsFields._id, direction: Direction.desc }, }, + [networkModel.NetworkTableType.http]: { + activePage: 0, + limit: 10, + sort: { direction: Direction.desc }, + }, }, }, details: { @@ -131,6 +136,11 @@ export const mockGlobalState: State = { limit: 10, sort: { field: UsersFields.name, direction: Direction.asc }, }, + [networkModel.IpDetailsTableType.http]: { + activePage: 0, + limit: 10, + sort: { direction: Direction.desc }, + }, }, }, }, diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index 30744b3e24c4b2..d3a242b41da7b5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -12,7 +12,6 @@ import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { inputsSelectors, State } from '../../../store'; - import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details'; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts index 9df57970176eb4..4f3d34f51fb932 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts @@ -5,9 +5,8 @@ */ import { StaticIndexPattern } from 'ui/index_patterns'; -import { Filter } from '@kbn/es-query'; import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { InputsModelId } from '../../../store/inputs/constants'; import { HostComponentProps } from '../../../components/link_to/redirect_to_hosts'; @@ -19,7 +18,7 @@ import { hostsModel } from '../../../store'; interface HostDetailsComponentReduxProps { query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; } interface HostBodyComponentDispatchProps { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts index 980c5535129aa5..afc577244f7e0e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts @@ -6,8 +6,7 @@ import { StaticIndexPattern } from 'ui/index_patterns'; import { ActionCreator } from 'typescript-fsa'; -import { Filter } from '@kbn/es-query'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { SiemPageName } from '../home/types'; import { hostsModel } from '../../store'; @@ -19,7 +18,7 @@ export const hostDetailsPagePath = `${hostsPagePath}/:detailName`; export interface HostsComponentReduxProps { query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; } export interface HostsComponentDispatchProps { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index 824539cad8d523..0fd4e073ebd13c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -38,6 +38,7 @@ export { getBreadcrumbs } from './utils'; import { TlsQueryTable } from './tls_query_table'; import { UsersQueryTable } from './users_query_table'; import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table'; +import { NetworkHttpQueryTable } from './network_http_query_table'; import { NetworkTopCountriesQueryTable } from './network_top_countries_query_table'; import { useKibanaCore } from '../../../lib/compose/kibana_core'; @@ -221,6 +222,18 @@ export const IPDetailsComponent = React.memo( + + + + ( + + {({ + id, + inspect, + isInspected, + loading, + loadPage, + networkHttp, + pageInfo, + refetch, + totalCount, + }) => ( + + )} + +); + +NetworkHttpQueryTable.displayName = 'NetworkHttpQueryTable'; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts index 008409197f77d4..e0029d8d219ebf 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { StaticIndexPattern } from 'ui/index_patterns'; import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/public'; import { NetworkType } from '../../../store/network/model'; import { ESTermQuery } from '../../../../common/typed_json'; @@ -25,7 +24,7 @@ type SetAbsoluteRangeDatePicker = ActionCreator<{ }>; interface IPDetailsComponentReduxProps { - filters: Filter[]; + filters: esFilters.Filter[]; flowTarget: FlowTarget; query: Query; } @@ -39,7 +38,7 @@ export type IPDetailsComponentProps = IPDetailsComponentReduxProps & IPDetailsComponentDispatchProps & GlobalTimeArgs & { detailName: string }; -interface OwnProps { +export interface OwnProps { type: NetworkType; startDate: number; endDate: number; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/http_query_tab_body.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/http_query_tab_body.tsx new file mode 100644 index 00000000000000..a20a212623fb84 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/http_query_tab_body.tsx @@ -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. + */ + +import React from 'react'; +import { getOr } from 'lodash/fp'; + +import { NetworkHttpTable } from '../../../components/page/network'; +import { NetworkHttpQuery } from '../../../containers/network_http'; +import { networkModel } from '../../../store'; +import { manageQuery } from '../../../components/page/manage_query'; + +import { HttpQueryTabBodyProps } from './types'; + +const NetworkHttpTableManage = manageQuery(NetworkHttpTable); + +export const HttpQueryTabBody = ({ + to, + filterQuery, + isInitializing, + from, + setQuery, +}: HttpQueryTabBodyProps) => ( + + {({ + id, + inspect, + isInspected, + loading, + loadPage, + networkHttp, + pageInfo, + refetch, + totalCount, + }) => ( + + )} + +); + +HttpQueryTabBody.displayName = 'HttpQueryTabBody'; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/nav_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/nav_tabs.tsx index ef097373b3ae7d..fbf137df398729 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/nav_tabs.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/nav_tabs.tsx @@ -26,6 +26,13 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => disabled: false, urlKey: 'network', }, + [NetworkRouteType.http]: { + id: NetworkRouteType.http, + name: i18n.NAVIGATION_HTTP_TITLE, + href: getTabsOnNetworkUrl(NetworkRouteType.http), + disabled: false, + urlKey: 'network', + }, [NetworkRouteType.tls]: { id: NetworkRouteType.tls, name: i18n.NAVIGATION_TLS_TITLE, diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx index fff9b1e7a09680..955670b4b098d0 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx @@ -13,6 +13,7 @@ import { scoreIntervalToDateTime } from '../../../components/ml/score/score_inte import { IPsQueryTabBody } from './ips_query_tab_body'; import { CountriesQueryTabBody } from './countries_query_tab_body'; +import { HttpQueryTabBody } from './http_query_tab_body'; import { AnomaliesQueryTabBody } from './anomalies_query_tab_body'; import { DnsQueryTabBody } from './dns_query_tab_body'; import { ConditionalFlexGroup } from './conditional_flex_group'; @@ -96,6 +97,10 @@ export const NetworkRoutes = ({ )} /> + } + /> } diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/navigation/types.ts index a3639da3cc1329..9682495b6f66a6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/types.ts @@ -34,6 +34,10 @@ export type TlsQueryTabBodyProps = QueryTabBodyProps & ip?: string; }; +export type HttpQueryTabBodyProps = QueryTabBodyProps & + GlobalTimeArgs & { + ip?: string; + }; export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & Pick & { narrowDateRange: NarrowDateRange; @@ -63,6 +67,7 @@ export enum NetworkRouteType { dns = 'dns', anomalies = 'anomalies', tls = 'tls', + http = 'http', } export type GetNetworkRoutePath = ( diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/utils.ts b/x-pack/legacy/plugins/siem/public/pages/network/navigation/utils.ts index a1cb9d61b9c479..059949bf518373 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/utils.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/utils.ts @@ -12,7 +12,7 @@ export const getNetworkRoutePath: GetNetworkRoutePath = ( hasMlUserPermission ) => { if (capabilitiesFetched && !hasMlUserPermission) { - return `${pagePath}/:tabName(${NetworkRouteType.flows}|${NetworkRouteType.dns}|${NetworkRouteType.tls})`; + return `${pagePath}/:tabName(${NetworkRouteType.flows}|${NetworkRouteType.dns}|${NetworkRouteType.http}|${NetworkRouteType.tls})`; } return ( @@ -20,6 +20,7 @@ export const getNetworkRoutePath: GetNetworkRoutePath = ( `${NetworkRouteType.flows}|` + `${NetworkRouteType.dns}|` + `${NetworkRouteType.anomalies}|` + + `${NetworkRouteType.http}|` + `${NetworkRouteType.tls})` ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/translations.ts b/x-pack/legacy/plugins/siem/public/pages/network/translations.ts index 25c117319248ab..be222bf5f2531f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/translations.ts @@ -39,6 +39,10 @@ export const NAVIGATION_TLS_TITLE = i18n.translate('xpack.siem.network.navigatio defaultMessage: 'TLS', }); +export const NAVIGATION_HTTP_TITLE = i18n.translate('xpack.siem.network.navigation.httpTitle', { + defaultMessage: 'HTTP', +}); + export const NAVIGATION_ANOMALIES_TITLE = i18n.translate( 'xpack.siem.network.navigation.anomaliesTitle', { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/types.ts index 46c868729b8328..e440d0c27e4676 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/types.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { RouteComponentProps } from 'react-router-dom'; import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; +import { Query, esFilters } from 'src/plugins/data/common'; import { GlobalTimeArgs } from '../../containers/global_time'; import { InputsModelId } from '../../store/inputs/constants'; @@ -19,7 +18,7 @@ export type SetAbsoluteRangeDatePicker = ActionCreator<{ }>; interface NetworkComponentReduxProps { - filters: Filter[]; + filters: esFilters.Filter[]; query: Query; setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; } diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts index 598b2854b96ebd..aefcd2ea8c696c 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import actionCreatorFactory from 'typescript-fsa'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { InspectQuery, Refetch } from './model'; import { InputsModelId } from './constants'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; const actionCreator = actionCreatorFactory('x-pack/siem/local/inputs'); @@ -83,5 +83,5 @@ export const setSavedQuery = actionCreator<{ export const setSearchBarFilter = actionCreator<{ id: InputsModelId; - filters: Filter[]; + filters: esFilters.Filter[]; }>('SET_SEARCH_BAR_FILTER'); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts index a98ea1f5d08121..01cf386311d77f 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '@kbn/es-query'; import { Dispatch } from 'redux'; import { Query } from 'src/plugins/data/common/query'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { Omit } from '../../../common/utility_types'; import { InputsModelId } from './constants'; import { CONSTANTS } from '../../components/url_state/constants'; +import { esFilters } from '../../../../../../../src/plugins/data/public'; export interface AbsoluteTimeRange { kind: 'absolute'; @@ -84,7 +84,7 @@ export interface InputsRange { queries: GlobalQuery[]; linkTo: InputsModelId[]; query: Query; - filters: Filter[]; + filters: esFilters.Filter[]; savedQuery?: SavedQuery; } diff --git a/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts b/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts index eb6a5c0c5c631e..a15e187b95e605 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts @@ -6,11 +6,11 @@ import { Direction, - NetworkTopTablesFields, + FlowTarget, NetworkDnsFields, + NetworkTopTablesFields, TlsFields, UsersFields, - FlowTarget, } from '../../graphql/types'; import { DEFAULT_TABLE_LIMIT } from '../constants'; import { NetworkModel, NetworkTableType, IpDetailsTableType, NetworkType } from './model'; @@ -68,6 +68,11 @@ export const mockNetworkState: NetworkModel = { direction: Direction.desc, }, }, + [NetworkTableType.http]: { + activePage: 0, + limit: DEFAULT_TABLE_LIMIT, + sort: { direction: Direction.desc }, + }, }, }, details: { @@ -120,6 +125,11 @@ export const mockNetworkState: NetworkModel = { direction: Direction.asc, }, }, + [IpDetailsTableType.http]: { + activePage: 0, + limit: DEFAULT_TABLE_LIMIT, + sort: { direction: Direction.desc }, + }, }, flowTarget: FlowTarget.source, }, @@ -145,6 +155,13 @@ describe('Network redux store', () => { sort: { field: 'uniqueDomains', direction: 'desc' }, isPtrIncluded: false, }, + [NetworkTableType.http]: { + activePage: 0, + limit: 10, + sort: { + direction: 'desc', + }, + }, [NetworkTableType.tls]: { activePage: 0, limit: 10, @@ -200,6 +217,13 @@ describe('Network redux store', () => { field: 'bytes_out', }, }, + [IpDetailsTableType.http]: { + activePage: 0, + limit: 10, + sort: { + direction: 'desc', + }, + }, [IpDetailsTableType.tls]: { activePage: 0, limit: 10, diff --git a/x-pack/legacy/plugins/siem/public/store/network/helpers.ts b/x-pack/legacy/plugins/siem/public/store/network/helpers.ts index c438fb6b492cea..0b3a5e65346b8f 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/helpers.ts @@ -40,6 +40,10 @@ export const setNetworkPageQueriesActivePageToZero = (state: NetworkModel): Netw ...state.page.queries[NetworkTableType.tls], activePage: DEFAULT_TABLE_ACTIVE_PAGE, }, + [NetworkTableType.http]: { + ...state.page.queries[NetworkTableType.http], + activePage: DEFAULT_TABLE_ACTIVE_PAGE, + }, }); export const setNetworkDetailsQueriesActivePageToZero = ( @@ -70,6 +74,10 @@ export const setNetworkDetailsQueriesActivePageToZero = ( ...state.details.queries[IpDetailsTableType.users], activePage: DEFAULT_TABLE_ACTIVE_PAGE, }, + [IpDetailsTableType.http]: { + ...state.details.queries[IpDetailsTableType.http], + activePage: DEFAULT_TABLE_ACTIVE_PAGE, + }, }); export const setNetworkQueriesActivePageToZero = ( diff --git a/x-pack/legacy/plugins/siem/public/store/network/model.ts b/x-pack/legacy/plugins/siem/public/store/network/model.ts index 1f82aa8a5319d3..45c49d65988811 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/model.ts @@ -7,6 +7,7 @@ import { FlowTarget, NetworkDnsSortField, + NetworkHttpSortField, NetworkTopTablesSortField, TlsSortField, UsersSortField, @@ -19,6 +20,7 @@ export enum NetworkType { export enum NetworkTableType { dns = 'dns', + http = 'http', topCountriesDestination = 'topCountriesDestination', topCountriesSource = 'topCountriesSource', topNFlowDestination = 'topNFlowDestination', @@ -40,7 +42,10 @@ export type TopCountriesTableType = export type TopTlsTableType = IpDetailsTableType.tls | NetworkTableType.tls; +export type HttpTableType = IpDetailsTableType.http | NetworkTableType.http; + export enum IpDetailsTableType { + http = 'http', tls = 'tls', topCountriesDestination = 'topCountriesDestination', topCountriesSource = 'topCountriesSource', @@ -74,15 +79,25 @@ export interface TlsQuery extends BasicQueryPaginated { sort: TlsSortField; } +export interface HttpQuery extends BasicQueryPaginated { + sort: NetworkHttpSortField; +} + export interface TableUpdates { activePage?: number; limit?: number; isPtrIncluded?: boolean; - sort?: NetworkDnsSortField | NetworkTopTablesSortField | TlsSortField | UsersSortField; + sort?: + | NetworkDnsSortField + | NetworkHttpSortField + | NetworkTopTablesSortField + | TlsSortField + | UsersSortField; } export interface NetworkQueries { [NetworkTableType.dns]: DnsQuery; + [NetworkTableType.http]: HttpQuery; [NetworkTableType.topCountriesDestination]: TopCountriesQuery; [NetworkTableType.topCountriesSource]: TopCountriesQuery; [NetworkTableType.topNFlowDestination]: TopNFlowQuery; @@ -99,6 +114,7 @@ export interface UsersQuery extends BasicQueryPaginated { } export interface IpOverviewQueries { + [IpDetailsTableType.http]: HttpQuery; [IpDetailsTableType.tls]: TlsQuery; [IpDetailsTableType.topCountriesDestination]: TopCountriesQuery; [IpDetailsTableType.topCountriesSource]: TopCountriesQuery; diff --git a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts index 88adc118d51dca..373bedb63c3e79 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts @@ -58,6 +58,13 @@ export const initialNetworkState: NetworkState = { }, isPtrIncluded: false, }, + [NetworkTableType.http]: { + activePage: DEFAULT_TABLE_ACTIVE_PAGE, + limit: DEFAULT_TABLE_LIMIT, + sort: { + direction: Direction.desc, + }, + }, [NetworkTableType.tls]: { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, @@ -86,6 +93,13 @@ export const initialNetworkState: NetworkState = { }, details: { queries: { + [IpDetailsTableType.http]: { + activePage: DEFAULT_TABLE_ACTIVE_PAGE, + limit: DEFAULT_TABLE_LIMIT, + sort: { + direction: Direction.desc, + }, + }, [IpDetailsTableType.topCountriesSource]: { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, diff --git a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts index 04cf8f0ea464c4..cf57c0d07c43e0 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts @@ -84,6 +84,21 @@ export const topCountriesSelector = () => topCountriesQueries => topCountriesQueries ); +const selectHttpByType = (state: State, networkType: NetworkType) => { + const httpType = + networkType === NetworkType.page ? NetworkTableType.http : IpDetailsTableType.http; + return ( + get([networkType, 'queries', httpType], state.network) || + get([networkType, 'queries', httpType], initialNetworkState) + ); +}; + +export const httpSelector = () => + createSelector( + selectHttpByType, + httpQueries => httpQueries + ); + // IP Details Selectors export const ipDetailsFlowTargetSelector = () => createSelector( diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts index 1c9493ceba9c3d..e5a712fe2c666f 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts @@ -127,9 +127,8 @@ export const epicPersistNote = ( export const createTimelineNoteEpic = (): Epic => action$ => action$.pipe( - withLatestFrom(), - filter(([action]) => timelineNoteActionsType.includes(action.type)), - switchMap(([action]) => { + filter(action => timelineNoteActionsType.includes(action.type)), + switchMap(action => { dispatcherTimelinePersistQueue.next({ action }); return empty(); }) diff --git a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts index 7ce88ba4880be1..db15babc42a722 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts @@ -20,6 +20,11 @@ type QueryNetworkTopNFlowResolver = ChildResolverOf< QuerySourceResolver >; +type QueryNetworkHttpResolver = ChildResolverOf< + AppResolverOf, + QuerySourceResolver +>; + type QueryDnsResolver = ChildResolverOf< AppResolverOf, QuerySourceResolver @@ -33,6 +38,7 @@ export const createNetworkResolvers = ( libs: NetworkResolversDeps ): { Source: { + NetworkHttp: QueryNetworkHttpResolver; NetworkTopCountries: QueryNetworkTopCountriesResolver; NetworkTopNFlow: QueryNetworkTopNFlowResolver; NetworkDns: QueryDnsResolver; @@ -57,6 +63,14 @@ export const createNetworkResolvers = ( }; return libs.network.getNetworkTopNFlow(req, options); }, + async NetworkHttp(source, args, { req }, info) { + const options = { + ...createOptionsPaginated(source, args, info), + networkHttpSort: args.sort, + ip: args.ip, + }; + return libs.network.getNetworkHttp(req, options); + }, async NetworkDns(source, args, { req }, info) { const options = { ...createOptionsPaginated(source, args, info), diff --git a/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts index 36b57ec9368abf..84f8d004198e93 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts @@ -152,6 +152,43 @@ export const networkSchema = gql` inspect: Inspect } + enum NetworkHttpFields { + domains + lastHost + lastSourceIp + methods + path + requestCount + statuses + } + + input NetworkHttpSortField { + direction: Direction! + } + + type NetworkHttpItem { + _id: String + domains: [String!]! + lastHost: String + lastSourceIp: String + methods: [String!]! + path: String + requestCount: Float + statuses: [String!]! + } + + type NetworkHttpEdges { + node: NetworkHttpItem! + cursor: CursorType! + } + + type NetworkHttpData { + edges: [NetworkHttpEdges!]! + totalCount: Float! + pageInfo: PageInfoPaginated! + inspect: Inspect + } + extend type Source { NetworkTopCountries( id: String @@ -182,5 +219,14 @@ export const networkSchema = gql` timerange: TimerangeInput! defaultIndex: [String!]! ): NetworkDnsData! + NetworkHttp( + id: String + filterQuery: String + ip: String + pagination: PaginationInputPaginated! + sort: NetworkHttpSortField! + timerange: TimerangeInput! + defaultIndex: [String!]! + ): NetworkHttpData! } `; diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index 6de11e06528713..cf7ce3ad02fa18 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -87,6 +87,10 @@ export interface NetworkDnsSortField { direction: Direction; } +export interface NetworkHttpSortField { + direction: Direction; +} + export interface TlsSortField { field: TlsFields; @@ -296,6 +300,16 @@ export enum NetworkDirectionEcs { unknown = 'unknown', } +export enum NetworkHttpFields { + domains = 'domains', + lastHost = 'lastHost', + lastSourceIp = 'lastSourceIp', + methods = 'methods', + path = 'path', + requestCount = 'requestCount', + statuses = 'statuses', +} + export enum FlowDirection { uniDirectional = 'uniDirectional', biDirectional = 'biDirectional', @@ -435,6 +449,8 @@ export interface Source { NetworkDns: NetworkDnsData; + NetworkHttp: NetworkHttpData; + OverviewNetwork?: Maybe; OverviewHost?: Maybe; @@ -1590,6 +1606,40 @@ export interface NetworkDnsItem { uniqueDomains?: Maybe; } +export interface NetworkHttpData { + edges: NetworkHttpEdges[]; + + totalCount: number; + + pageInfo: PageInfoPaginated; + + inspect?: Maybe; +} + +export interface NetworkHttpEdges { + node: NetworkHttpItem; + + cursor: CursorType; +} + +export interface NetworkHttpItem { + _id?: Maybe; + + domains: string[]; + + lastHost?: Maybe; + + lastSourceIp?: Maybe; + + methods: string[]; + + path?: Maybe; + + requestCount?: Maybe; + + statuses: string[]; +} + export interface OverviewNetworkData { auditbeatSocket?: Maybe; @@ -2164,6 +2214,21 @@ export interface NetworkDnsSourceArgs { defaultIndex: string[]; } +export interface NetworkHttpSourceArgs { + id?: Maybe; + + filterQuery?: Maybe; + + ip?: Maybe; + + pagination: PaginationInputPaginated; + + sort: NetworkHttpSortField; + + timerange: TimerangeInput; + + defaultIndex: string[]; +} export interface OverviewNetworkSourceArgs { id?: Maybe; @@ -2643,6 +2708,8 @@ export namespace SourceResolvers { NetworkDns?: NetworkDnsResolver; + NetworkHttp?: NetworkHttpResolver; + OverviewNetwork?: OverviewNetworkResolver, TypeParent, TContext>; OverviewHost?: OverviewHostResolver, TypeParent, TContext>; @@ -2956,6 +3023,27 @@ export namespace SourceResolvers { defaultIndex: string[]; } + export type NetworkHttpResolver< + R = NetworkHttpData, + Parent = Source, + TContext = SiemContext + > = Resolver; + export interface NetworkHttpArgs { + id?: Maybe; + + filterQuery?: Maybe; + + ip?: Maybe; + + pagination: PaginationInputPaginated; + + sort: NetworkHttpSortField; + + timerange: TimerangeInput; + + defaultIndex: string[]; + } + export type OverviewNetworkResolver< R = Maybe, Parent = Source, @@ -6863,6 +6951,119 @@ export namespace NetworkDnsItemResolvers { > = Resolver; } +export namespace NetworkHttpDataResolvers { + export interface Resolvers { + edges?: EdgesResolver; + + totalCount?: TotalCountResolver; + + pageInfo?: PageInfoResolver; + + inspect?: InspectResolver, TypeParent, TContext>; + } + + export type EdgesResolver< + R = NetworkHttpEdges[], + Parent = NetworkHttpData, + TContext = SiemContext + > = Resolver; + export type TotalCountResolver< + R = number, + Parent = NetworkHttpData, + TContext = SiemContext + > = Resolver; + export type PageInfoResolver< + R = PageInfoPaginated, + Parent = NetworkHttpData, + TContext = SiemContext + > = Resolver; + export type InspectResolver< + R = Maybe, + Parent = NetworkHttpData, + TContext = SiemContext + > = Resolver; +} + +export namespace NetworkHttpEdgesResolvers { + export interface Resolvers { + node?: NodeResolver; + + cursor?: CursorResolver; + } + + export type NodeResolver< + R = NetworkHttpItem, + Parent = NetworkHttpEdges, + TContext = SiemContext + > = Resolver; + export type CursorResolver< + R = CursorType, + Parent = NetworkHttpEdges, + TContext = SiemContext + > = Resolver; +} + +export namespace NetworkHttpItemResolvers { + export interface Resolvers { + _id?: _IdResolver, TypeParent, TContext>; + + domains?: DomainsResolver; + + lastHost?: LastHostResolver, TypeParent, TContext>; + + lastSourceIp?: LastSourceIpResolver, TypeParent, TContext>; + + methods?: MethodsResolver; + + path?: PathResolver, TypeParent, TContext>; + + requestCount?: RequestCountResolver, TypeParent, TContext>; + + statuses?: StatusesResolver; + } + + export type _IdResolver< + R = Maybe, + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type DomainsResolver< + R = string[], + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type LastHostResolver< + R = Maybe, + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type LastSourceIpResolver< + R = Maybe, + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type MethodsResolver< + R = string[], + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type PathResolver< + R = Maybe, + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type RequestCountResolver< + R = Maybe, + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; + export type StatusesResolver< + R = string[], + Parent = NetworkHttpItem, + TContext = SiemContext + > = Resolver; +} + export namespace OverviewNetworkDataResolvers { export interface Resolvers { auditbeatSocket?: AuditbeatSocketResolver, TypeParent, TContext>; @@ -8265,6 +8466,9 @@ export type IResolvers = { NetworkDnsData?: NetworkDnsDataResolvers.Resolvers; NetworkDnsEdges?: NetworkDnsEdgesResolvers.Resolvers; NetworkDnsItem?: NetworkDnsItemResolvers.Resolvers; + NetworkHttpData?: NetworkHttpDataResolvers.Resolvers; + NetworkHttpEdges?: NetworkHttpEdgesResolvers.Resolvers; + NetworkHttpItem?: NetworkHttpItemResolvers.Resolvers; OverviewNetworkData?: OverviewNetworkDataResolvers.Resolvers; OverviewHostData?: OverviewHostDataResolvers.Resolvers; TlsData?: TlsDataResolvers.Resolvers; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts index c1f0b44770ca8d..2caf00ed0179cf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts @@ -124,6 +124,7 @@ export const createSignals = async ({ return alertsClient.create({ data: { + name: 'SIEM Alert', alertTypeId: SIGNALS_ID, alertTypeParams: { description, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts index 7db2db5538dbf3..b8d7af5c453033 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts @@ -7,7 +7,7 @@ import { get } from 'lodash/fp'; import Hapi from 'hapi'; -import { Filter } from '@kbn/es-query'; +import { esFilters } from '../../../../../../../../src/plugins/data/common'; import { SIGNALS_ID } from '../../../../common/constants'; import { Alert, @@ -19,7 +19,7 @@ import { AlertsClient } from '../../../../../alerting/server/alerts_client'; import { ActionsClient } from '../../../../../actions/server/actions_client'; import { SearchResponse } from '../../types'; -export type PartialFilter = Partial; +export type PartialFilter = Partial; export interface SignalAlertParams { description: string; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts index cb777ad4003935..9cb30a29173d70 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts @@ -84,6 +84,7 @@ export const updateSignal = async ({ return alertsClient.update({ id: signal.id, data: { + name: 'SIEM Alert', interval: calculateInterval(interval, signal.interval), actions, alertTypeParams: nextAlertTypeParams, diff --git a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts index eff5fba0c54d52..39babc58ee138c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts @@ -15,6 +15,8 @@ import { NetworkTopCountriesData, NetworkTopCountriesEdges, NetworkTopNFlowData, + NetworkHttpData, + NetworkHttpEdges, NetworkTopNFlowEdges, } from '../../graphql/types'; import { inspectStringifyObject } from '../../utils/build_query'; @@ -25,15 +27,18 @@ import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; import { NetworkDnsRequestOptions, NetworkTopCountriesRequestOptions, + NetworkHttpRequestOptions, NetworkTopNFlowRequestOptions, } from './index'; import { buildDnsQuery } from './query_dns.dsl'; import { buildTopNFlowQuery, getOppositeField } from './query_top_n_flow.dsl'; +import { buildHttpQuery } from './query_http.dsl'; import { buildTopCountriesQuery } from './query_top_countries.dsl'; import { NetworkAdapter, NetworkDnsBuckets, NetworkTopCountriesBuckets, + NetworkHttpBuckets, NetworkTopNFlowBuckets, } from './types'; @@ -151,6 +156,42 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter { totalCount, }; } + + public async getNetworkHttp( + request: FrameworkRequest, + options: NetworkHttpRequestOptions + ): Promise { + if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { + throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); + } + const dsl = buildHttpQuery(options); + const response = await this.framework.callWithRequest( + request, + 'search', + dsl + ); + const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; + const totalCount = getOr(0, 'aggregations.http_count.value', response); + const networkHttpEdges: NetworkHttpEdges[] = getHttpEdges(response); + const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; + const edges = networkHttpEdges.splice(cursorStart, querySize - cursorStart); + const inspect = { + dsl: [inspectStringifyObject(dsl)], + response: [inspectStringifyObject(response)], + }; + const showMorePagesIndicator = totalCount > fakeTotalCount; + + return { + edges, + inspect, + pageInfo: { + activePage: activePage ? activePage : 0, + fakeTotalCount, + showMorePagesIndicator, + }, + totalCount, + }; + } } const getTopNFlowEdges = ( @@ -173,6 +214,12 @@ const getTopCountriesEdges = ( ); }; +const getHttpEdges = ( + response: DatabaseSearchResponse +): NetworkHttpEdges[] => { + return formatHttpEdges(getOr([], `aggregations.url.buckets`, response)); +}; + const getFlowTargetFromString = (flowAsString: string) => flowAsString === 'source' ? FlowTargetSourceDest.source : FlowTargetSourceDest.destination; @@ -287,6 +334,24 @@ const formatDnsEdges = (buckets: NetworkDnsBuckets[]): NetworkDnsEdges[] => }, })); +const formatHttpEdges = (buckets: NetworkHttpBuckets[]): NetworkHttpEdges[] => + buckets.map((bucket: NetworkHttpBuckets) => ({ + node: { + _id: bucket.key, + domains: bucket.domains.buckets.map(({ key }) => key), + methods: bucket.methods.buckets.map(({ key }) => key), + statuses: bucket.status.buckets.map(({ key }) => `${key}`), + lastHost: get('source.hits.hits[0]._source.host.name', bucket), + lastSourceIp: get('source.hits.hits[0]._source.source.ip', bucket), + path: bucket.key, + requestCount: bucket.doc_count, + }, + cursor: { + value: bucket.key, + tiebreaker: null, + }, + })); + const getOrNumber = (path: string, bucket: NetworkTopNFlowBuckets | NetworkDnsBuckets) => { const numb = get(path, bucket); if (numb == null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/network/index.ts b/x-pack/legacy/plugins/siem/server/lib/network/index.ts index e391dd922ce31c..0a2de936799b60 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/index.ts @@ -7,7 +7,10 @@ import { FlowTargetSourceDest, Maybe, + NetworkDnsData, NetworkDnsSortField, + NetworkHttpData, + NetworkHttpSortField, NetworkTopCountriesData, NetworkTopNFlowData, NetworkTopTablesSortField, @@ -30,6 +33,11 @@ export interface NetworkTopCountriesRequestOptions extends RequestOptionsPaginat ip?: Maybe; } +export interface NetworkHttpRequestOptions extends RequestOptionsPaginated { + networkHttpSort: NetworkHttpSortField; + ip?: Maybe; +} + export interface NetworkDnsRequestOptions extends RequestOptionsPaginated { isPtrIncluded: boolean; networkDnsSortField: NetworkDnsSortField; @@ -55,7 +63,14 @@ export class Network { public async getNetworkDns( req: FrameworkRequest, options: NetworkDnsRequestOptions - ): Promise { + ): Promise { return this.adapter.getNetworkDns(req, options); } + + public async getNetworkHttp( + req: FrameworkRequest, + options: NetworkHttpRequestOptions + ): Promise { + return this.adapter.getNetworkHttp(req, options); + } } diff --git a/x-pack/legacy/plugins/siem/server/lib/network/query_http.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/network/query_http.dsl.ts new file mode 100644 index 00000000000000..3e33b5af80a855 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/network/query_http.dsl.ts @@ -0,0 +1,112 @@ +/* + * 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 { NetworkHttpSortField } from '../../graphql/types'; +import { createQueryFilterClauses } from '../../utils/build_query'; + +import { NetworkHttpRequestOptions } from './index'; + +const getCountAgg = () => ({ + http_count: { + cardinality: { + field: 'url.path', + }, + }, +}); + +export const buildHttpQuery = ({ + defaultIndex, + filterQuery, + networkHttpSort, + pagination: { querySize }, + sourceConfiguration: { + fields: { timestamp }, + }, + timerange: { from, to }, + ip, +}: NetworkHttpRequestOptions) => { + const filter = [ + ...createQueryFilterClauses(filterQuery), + { range: { [timestamp]: { gte: from, lte: to } } }, + { exists: { field: 'http.request.method' } }, + ]; + + const dslQuery = { + allowNoIndices: true, + index: defaultIndex, + ignoreUnavailable: true, + body: { + aggregations: { + ...getCountAgg(), + ...getHttpAggs(networkHttpSort, querySize), + }, + query: { + bool: ip + ? { + filter, + should: [ + { + term: { + 'source.ip': ip, + }, + }, + { + term: { + 'destination.ip': ip, + }, + }, + ], + minimum_should_match: 1, + } + : { + filter, + }, + }, + }, + size: 0, + track_total_hits: false, + }; + return dslQuery; +}; + +const getHttpAggs = (networkHttpSortField: NetworkHttpSortField, querySize: number) => ({ + url: { + terms: { + field: `url.path`, + size: querySize, + order: { + _count: networkHttpSortField.direction, + }, + }, + aggs: { + methods: { + terms: { + field: 'http.request.method', + size: 4, + }, + }, + domains: { + terms: { + field: 'url.domain', + size: 4, + }, + }, + status: { + terms: { + field: 'http.response.status_code', + size: 4, + }, + }, + source: { + top_hits: { + size: 1, + _source: { + includes: ['host.name', 'source.ip'], + }, + }, + }, + }, + }, +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/network/types.ts b/x-pack/legacy/plugins/siem/server/lib/network/types.ts index e2e443fb502591..6afcb211b59961 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/types.ts @@ -4,7 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { NetworkTopCountriesData, NetworkDnsData, NetworkTopNFlowData } from '../../graphql/types'; +import { + NetworkDnsData, + NetworkHttpData, + NetworkTopCountriesData, + NetworkTopNFlowData, +} from '../../graphql/types'; import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { TotalValue } from '../types'; @@ -18,6 +23,7 @@ export interface NetworkAdapter { options: RequestOptionsPaginated ): Promise; getNetworkDns(req: FrameworkRequest, options: RequestOptionsPaginated): Promise; + getNetworkHttp(req: FrameworkRequest, options: RequestOptionsPaginated): Promise; } export interface GenericBuckets { @@ -61,6 +67,21 @@ interface AutonomousSystemHit { }; } +interface HttpHit { + hits: { + total: TotalValue | number; + max_score: number | null; + hits: Array<{ + _source: T; + sort?: [number]; + _index?: string; + _type?: string; + _id?: string; + _score?: number | null; + }>; + }; +} + export interface NetworkTopNFlowBuckets { key: string; autonomous_system: AutonomousSystemHit; @@ -106,3 +127,18 @@ export interface NetworkDnsBuckets { value: number; }; } + +export interface NetworkHttpBuckets { + key: string; + doc_count: number; + domains: { + buckets: GenericBuckets[]; + }; + methods: { + buckets: GenericBuckets[]; + }; + source: HttpHit; + status: { + buckets: GenericBuckets[]; + }; +} diff --git a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts index 2be7724f4097fe..53ed7c26b877f6 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts @@ -6,7 +6,7 @@ import { cloneDeep, isArray } from 'lodash/fp'; -import { convertSchemaToAssociativeArray, getIndexSchemaDoc } from '.'; +import { convertSchemaToAssociativeArray, getIndexSchemaDoc, getIndexAlias } from '.'; import { auditbeatSchema, filebeatSchema, packetbeatSchema } from './8.0.0'; import { Schema } from './type'; @@ -657,4 +657,17 @@ describe('Schema Beat', () => { ]); }); }); + + describe('getIndexAlias', () => { + test('getIndexAlias handles values with leading wildcard', () => { + const leadingWildcardIndex = '*-auditbeat-*'; + const result = getIndexAlias([leadingWildcardIndex], leadingWildcardIndex); + expect(result).toBe(leadingWildcardIndex); + }); + test('getIndexAlias no match returns "unknown" string', () => { + const index = 'auditbeat-*'; + const result = getIndexAlias([index], 'hello'); + expect(result).toBe('unknown'); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts index aaa171c6befd9c..a191bd835a7c73 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts @@ -77,7 +77,7 @@ const convertFieldsToAssociativeArray = ( : {}; export const getIndexAlias = (defaultIndex: string[], indexName: string): string => { - const found = defaultIndex.find(index => indexName.match(index) != null); + const found = defaultIndex.find(index => `\\${indexName}`.match(`\\${index}`) != null); if (found != null) { return found; } else { diff --git a/x-pack/legacy/plugins/task_manager/lib/fill_pool.test.ts b/x-pack/legacy/plugins/task_manager/lib/fill_pool.test.ts index 860a637d581088..d7ac8d227fc4c0 100644 --- a/x-pack/legacy/plugins/task_manager/lib/fill_pool.test.ts +++ b/x-pack/legacy/plugins/task_manager/lib/fill_pool.test.ts @@ -7,13 +7,14 @@ import _ from 'lodash'; import sinon from 'sinon'; import { fillPool } from './fill_pool'; +import { TaskPoolRunResult } from '../task_pool'; describe('fillPool', () => { test('stops filling when there are no more tasks in the store', async () => { const tasks = [[1, 2, 3], [4, 5]]; let index = 0; const fetchAvailableTasks = async () => tasks[index++] || []; - const run = sinon.spy(async () => true); + const run = sinon.spy(async () => TaskPoolRunResult.RunningAllClaimedTasks); const converter = _.identity; await fillPool(run, fetchAvailableTasks, converter); @@ -25,7 +26,7 @@ describe('fillPool', () => { const tasks = [[1, 2, 3], [4, 5]]; let index = 0; const fetchAvailableTasks = async () => tasks[index++] || []; - const run = sinon.spy(async () => false); + const run = sinon.spy(async () => TaskPoolRunResult.RanOutOfCapacity); const converter = _.identity; await fillPool(run, fetchAvailableTasks, converter); @@ -37,7 +38,7 @@ describe('fillPool', () => { const tasks = [[1, 2, 3], [4, 5]]; let index = 0; const fetchAvailableTasks = async () => tasks[index++] || []; - const run = sinon.spy(async () => false); + const run = sinon.spy(async () => TaskPoolRunResult.RanOutOfCapacity); const converter = (x: number) => x.toString(); await fillPool(run, fetchAvailableTasks, converter); @@ -47,7 +48,7 @@ describe('fillPool', () => { describe('error handling', () => { test('throws exception from fetchAvailableTasks', async () => { - const run = sinon.spy(async () => false); + const run = sinon.spy(async () => TaskPoolRunResult.RanOutOfCapacity); const converter = (x: number) => x.toString(); try { @@ -80,7 +81,7 @@ describe('fillPool', () => { const tasks = [[1, 2, 3], [4, 5]]; let index = 0; const fetchAvailableTasks = async () => tasks[index++] || []; - const run = sinon.spy(async () => false); + const run = sinon.spy(async () => TaskPoolRunResult.RanOutOfCapacity); const converter = (x: number) => { throw new Error(`can not convert ${x}`); }; diff --git a/x-pack/legacy/plugins/task_manager/lib/fill_pool.ts b/x-pack/legacy/plugins/task_manager/lib/fill_pool.ts index a5970574abaaf4..6fe965e048ea58 100644 --- a/x-pack/legacy/plugins/task_manager/lib/fill_pool.ts +++ b/x-pack/legacy/plugins/task_manager/lib/fill_pool.ts @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -type BatchRun = (tasks: T[]) => Promise; +import { performance } from 'perf_hooks'; +import { TaskPoolRunResult } from '../task_pool'; + +export enum FillPoolResult { + NoTasksClaimed = 'NoTasksClaimed', + RanOutOfCapacity = 'RanOutOfCapacity', +} + +type BatchRun = (tasks: T[]) => Promise; type Fetcher = () => Promise; type Converter = (t: T1) => T2; @@ -24,18 +32,32 @@ export async function fillPool( run: BatchRun, fetchAvailableTasks: Fetcher, converter: Converter -): Promise { +): Promise { + performance.mark('fillPool.start'); while (true) { const instances = await fetchAvailableTasks(); if (!instances.length) { - return; + performance.mark('fillPool.bailNoTasks'); + performance.measure( + 'fillPool.activityDurationUntilNoTasks', + 'fillPool.start', + 'fillPool.bailNoTasks' + ); + return FillPoolResult.NoTasksClaimed; } const tasks = instances.map(converter); - if (!(await run(tasks))) { - return; + if ((await run(tasks)) === TaskPoolRunResult.RanOutOfCapacity) { + performance.mark('fillPool.bailExhaustedCapacity'); + performance.measure( + 'fillPool.activityDurationUntilExhaustedCapacity', + 'fillPool.start', + 'fillPool.bailExhaustedCapacity' + ); + return FillPoolResult.RanOutOfCapacity; } + performance.mark('fillPool.cycle'); } } diff --git a/x-pack/legacy/plugins/task_manager/lib/identify_es_error.test.ts b/x-pack/legacy/plugins/task_manager/lib/identify_es_error.test.ts new file mode 100644 index 00000000000000..453b81030311dd --- /dev/null +++ b/x-pack/legacy/plugins/task_manager/lib/identify_es_error.test.ts @@ -0,0 +1,167 @@ +/* + * 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 { identifyEsError, ESErrorCausedBy } from './identify_es_error'; + +describe('identifyEsError', () => { + test('extracts messages from root cause', () => { + expect( + identifyEsError( + generateESErrorWithResponse( + [ + { + type: 'illegal_argument_exception', + reason: 'root cause', + }, + ], + {} + ) + ) + ).toContain('root cause'); + }); + + test('extracts messages from deep root cause', () => { + expect( + identifyEsError( + generateESErrorWithResponse( + [ + { + type: 'illegal_argument_exception', + reason: 'root cause', + }, + { + type: 'illegal_argument_exception', + reason: 'deep root cause', + }, + ], + {} + ) + ) + ).toContain('deep root cause'); + }); + + test('extracts messages from first caused by', () => { + expect( + identifyEsError( + generateESErrorWithResponse( + [ + { + type: 'illegal_argument_exception', + reason: 'root cause', + }, + { + type: 'illegal_argument_exception', + reason: 'deep root cause', + }, + ], + { + type: 'illegal_argument_exception', + reason: 'first caused by', + caused_by: { + type: 'illegal_argument_exception', + reason: 'second caused by', + }, + } + ) + ) + ).toContain('first caused by'); + }); + + test('extracts messages from deep caused by', () => { + expect( + identifyEsError( + generateESErrorWithResponse( + [ + { + type: 'illegal_argument_exception', + reason: 'root cause', + }, + { + type: 'illegal_argument_exception', + reason: 'deep root cause', + }, + ], + { + type: 'illegal_argument_exception', + reason: 'first caused by', + caused_by: { + type: 'illegal_argument_exception', + reason: 'second caused by', + }, + } + ) + ) + ).toContain('second caused by'); + }); + + test('extracts all messages in error', () => { + expect( + identifyEsError( + generateESErrorWithResponse( + [ + { + type: 'illegal_argument_exception', + reason: 'root cause', + }, + { + type: 'illegal_argument_exception', + reason: 'deep root cause', + }, + ], + { + type: 'illegal_argument_exception', + reason: 'first caused by', + caused_by: { + type: 'illegal_argument_exception', + reason: 'second caused by', + }, + } + ) + ) + ).toMatchInlineSnapshot(` + Array [ + "first caused by", + "second caused by", + "root cause", + "deep root cause", + ] + `); + }); +}); + +function generateESErrorWithResponse( + rootCause: ESErrorCausedBy[] = [], + causeBy: ESErrorCausedBy = {} +) { + return Object.assign(new Error(), { + msg: '[illegal_argument_exception] cannot execute [inline] scripts', + path: '/.kibana_task_manager/_update_by_query', + query: {}, + body: '{"query":{}}', + statusCode: 400, + response: JSON.stringify({ + error: { + root_cause: rootCause, + type: 'search_phase_execution_exception', + reason: 'all shards failed', + phase: 'query', + grouped: true, + failed_shards: [ + { + shard: 0, + index: '.kibana_task_manager_1', + node: '24A4QbjHSK6prvtopAKLKw', + reason: { + type: 'illegal_argument_exception', + reason: 'cannot execute [inline] scripts', + }, + }, + ], + caused_by: causeBy, + }, + status: 400, + }), + }); +} diff --git a/x-pack/legacy/plugins/task_manager/lib/identify_es_error.ts b/x-pack/legacy/plugins/task_manager/lib/identify_es_error.ts new file mode 100644 index 00000000000000..93baa7a94a337f --- /dev/null +++ b/x-pack/legacy/plugins/task_manager/lib/identify_es_error.ts @@ -0,0 +1,59 @@ +/* + * 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 interface ESErrorCausedBy { + type?: string; + reason?: string; + caused_by?: ESErrorCausedBy; +} + +export interface ESError { + root_cause?: ESErrorCausedBy[]; + caused_by?: ESErrorCausedBy; +} + +function extractCausedByChain( + causedBy: ESErrorCausedBy = {}, + accumulator: string[] = [] +): string[] { + const { reason, caused_by: innerCausedBy } = causedBy; + + if (reason) { + accumulator.push(reason); + } + + if (innerCausedBy) { + return extractCausedByChain(innerCausedBy, accumulator); + } + + return accumulator; +} + +/** + * Identified causes for ES Error + * + * @param err Object Error thrown by ES JS client + * @return ES error cause + */ +export function identifyEsError(err: { response: string }) { + const { response } = err; + + if (response) { + const { error } = JSON.parse(response) as { error?: ESError }; + if (error) { + const { root_cause: rootCause = [], caused_by: causedBy } = error; + + return [ + ...extractCausedByChain(causedBy), + ...rootCause.reduce( + (acc: string[], innerRootCause) => extractCausedByChain(innerRootCause, acc), + [] + ), + ]; + } + } + return []; +} diff --git a/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts b/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts index 07afee17974626..699765f16d83e5 100644 --- a/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts +++ b/x-pack/legacy/plugins/task_manager/lib/middleware.test.ts @@ -70,6 +70,7 @@ describe('addMiddlewareToChain', () => { return opts; }, beforeRun: defaultBeforeRun, + beforeMarkRunning: defaultBeforeRun, }; const m2 = { beforeSave: async (opts: BeforeSaveOpts) => { @@ -77,6 +78,7 @@ describe('addMiddlewareToChain', () => { return opts; }, beforeRun: defaultBeforeRun, + beforeMarkRunning: defaultBeforeRun, }; const m3 = { beforeSave: async (opts: BeforeSaveOpts) => { @@ -84,6 +86,7 @@ describe('addMiddlewareToChain', () => { return opts; }, beforeRun: defaultBeforeRun, + beforeMarkRunning: defaultBeforeRun, }; let middlewareChain; @@ -119,6 +122,7 @@ describe('addMiddlewareToChain', () => { m1: true, }; }, + beforeMarkRunning: defaultBeforeRun, }; const m2 = { beforeSave: defaultBeforeSave, @@ -128,6 +132,7 @@ describe('addMiddlewareToChain', () => { m2: true, }; }, + beforeMarkRunning: defaultBeforeRun, }; const m3 = { beforeSave: defaultBeforeSave, @@ -137,6 +142,7 @@ describe('addMiddlewareToChain', () => { m3: true, }; }, + beforeMarkRunning: defaultBeforeRun, }; let middlewareChain; diff --git a/x-pack/legacy/plugins/task_manager/lib/middleware.ts b/x-pack/legacy/plugins/task_manager/lib/middleware.ts index d81b76fda7e50e..d367c8ca56c090 100644 --- a/x-pack/legacy/plugins/task_manager/lib/middleware.ts +++ b/x-pack/legacy/plugins/task_manager/lib/middleware.ts @@ -23,10 +23,12 @@ export type BeforeSaveFunction = ( ) => Promise; export type BeforeRunFunction = (params: RunContext) => Promise; +export type BeforeMarkRunningFunction = (params: RunContext) => Promise; export interface Middleware { beforeSave: BeforeSaveFunction; beforeRun: BeforeRunFunction; + beforeMarkRunning: BeforeMarkRunningFunction; } export function addMiddlewareToChain(prevMiddleware: Middleware, middleware: Middleware) { @@ -39,8 +41,14 @@ export function addMiddlewareToChain(prevMiddleware: Middleware, middleware: Mid ? (params: RunContext) => middleware.beforeRun(params).then(prevMiddleware.beforeRun) : prevMiddleware.beforeRun; + const beforeMarkRunning = middleware.beforeMarkRunning + ? (params: RunContext) => + middleware.beforeMarkRunning(params).then(prevMiddleware.beforeMarkRunning) + : prevMiddleware.beforeMarkRunning; + return { beforeSave, beforeRun, + beforeMarkRunning, }; } diff --git a/x-pack/legacy/plugins/task_manager/task_manager.test.ts b/x-pack/legacy/plugins/task_manager/task_manager.test.ts index 8592ff31d700ff..9ae2f5e1e027b5 100644 --- a/x-pack/legacy/plugins/task_manager/task_manager.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_manager.test.ts @@ -174,6 +174,7 @@ describe('TaskManager', () => { const middleware = { beforeSave: async (saveOpts: any) => saveOpts, beforeRun: async (runOpts: any) => runOpts, + beforeMarkRunning: async (runOpts: any) => runOpts, }; expect(() => client.addMiddleware(middleware)).not.toThrow(); }); @@ -183,6 +184,7 @@ describe('TaskManager', () => { const middleware = { beforeSave: async (saveOpts: any) => saveOpts, beforeRun: async (runOpts: any) => runOpts, + beforeMarkRunning: async (runOpts: any) => runOpts, }; client.start(); @@ -214,5 +216,37 @@ describe('TaskManager', () => { expect(claim).not.toHaveBeenCalled(); }); + + /** + * This handles the case in which Elasticsearch has had inline script disabled. + * This is achieved by setting the `script.allowed_types` flag on Elasticsearch to `none` + */ + test('handles failure due to inline scripts being disabled', () => { + const logger = mockLogger(); + const claim = jest.fn(() => { + throw Object.assign(new Error(), { + msg: '[illegal_argument_exception] cannot execute [inline] scripts', + path: '/.kibana_task_manager/_update_by_query', + query: { + ignore_unavailable: true, + refresh: true, + max_docs: 200, + conflicts: 'proceed', + }, + body: + '{"query":{"bool":{"must":[{"term":{"type":"task"}},{"bool":{"must":[{"bool":{"should":[{"bool":{"must":[{"term":{"task.status":"idle"}},{"range":{"task.runAt":{"lte":"now"}}}]}},{"bool":{"must":[{"bool":{"should":[{"term":{"task.status":"running"}},{"term":{"task.status":"claiming"}}]}},{"range":{"task.retryAt":{"lte":"now"}}}]}}]}},{"bool":{"should":[{"exists":{"field":"task.interval"}},{"bool":{"must":[{"term":{"task.taskType":"vis_telemetry"}},{"range":{"task.attempts":{"lt":3}}}]}},{"bool":{"must":[{"term":{"task.taskType":"lens_telemetry"}},{"range":{"task.attempts":{"lt":3}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.server-log"}},{"range":{"task.attempts":{"lt":1}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.slack"}},{"range":{"task.attempts":{"lt":1}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.email"}},{"range":{"task.attempts":{"lt":1}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.index"}},{"range":{"task.attempts":{"lt":1}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.pagerduty"}},{"range":{"task.attempts":{"lt":1}}}]}},{"bool":{"must":[{"term":{"task.taskType":"actions:.webhook"}},{"range":{"task.attempts":{"lt":1}}}]}}]}}]}}]}},"sort":{"_script":{"type":"number","order":"asc","script":{"lang":"expression","source":"doc[\'task.retryAt\'].value || doc[\'task.runAt\'].value"}}},"seq_no_primary_term":true,"script":{"source":"ctx._source.task.ownerId=params.ownerId; ctx._source.task.status=params.status; ctx._source.task.retryAt=params.retryAt;","lang":"painless","params":{"ownerId":"kibana:5b2de169-2785-441b-ae8c-186a1936b17d","retryAt":"2019-10-31T13:35:43.579Z","status":"claiming"}}}', + statusCode: 400, + response: + '{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"cannot execute [inline] scripts"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":".kibana_task_manager_1","node":"24A4QbjHSK6prvtopAKLKw","reason":{"type":"illegal_argument_exception","reason":"cannot execute [inline] scripts"}}],"caused_by":{"type":"illegal_argument_exception","reason":"cannot execute [inline] scripts","caused_by":{"type":"illegal_argument_exception","reason":"cannot execute [inline] scripts"}}},"status":400}', + }); + }); + + claimAvailableTasks(claim, 10, logger); + + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Task Manager cannot operate when inline scripts are disabled in Elasticsearch"` + ); + }); }); }); diff --git a/x-pack/legacy/plugins/task_manager/task_manager.ts b/x-pack/legacy/plugins/task_manager/task_manager.ts index 41b08cda98f219..4ddb18c7cfe748 100644 --- a/x-pack/legacy/plugins/task_manager/task_manager.ts +++ b/x-pack/legacy/plugins/task_manager/task_manager.ts @@ -3,10 +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 { performance } from 'perf_hooks'; import { SavedObjectsClientContract, SavedObjectsSerializer } from 'src/core/server'; import { Logger } from './types'; -import { fillPool } from './lib/fill_pool'; +import { fillPool, FillPoolResult } from './lib/fill_pool'; import { addMiddlewareToChain, BeforeSaveMiddlewareParams, Middleware } from './lib/middleware'; import { sanitizeTaskDefinitions } from './lib/sanitize_task_definitions'; import { intervalFromNow } from './lib/intervals'; @@ -27,6 +27,7 @@ import { OwnershipClaimingOpts, ClaimOwnershipResult, } from './task_store'; +import { identifyEsError } from './lib/identify_es_error'; export interface TaskManagerOpts { logger: Logger; @@ -55,13 +56,14 @@ export class TaskManager { private readonly pollerInterval: number; private definitions: TaskDictionary; private store: TaskStore; - private poller: TaskPoller; + private poller: TaskPoller; private logger: Logger; private pool: TaskPool; private startQueue: Array<() => void> = []; private middleware = { beforeSave: async (saveOpts: BeforeSaveMiddlewareParams) => saveOpts, beforeRun: async (runOpts: RunContext) => runOpts, + beforeMarkRunning: async (runOpts: RunContext) => runOpts, }; /** @@ -85,8 +87,6 @@ export class TaskManager { this.logger.info(`TaskManager is identified by the Kibana UUID: ${taskManagerId}`); } - /* Kibana UUID needs to be pulled live (not cached), as it takes a long time - * to initialize, and can change after startup */ const store = new TaskStore({ serializer: opts.serializer, savedObjectsRepository: opts.savedObjectsRepository, @@ -108,13 +108,14 @@ export class TaskManager { store, definitions: this.definitions, beforeRun: this.middleware.beforeRun, + beforeMarkRunning: this.middleware.beforeMarkRunning, }); - const poller = new TaskPoller({ + const poller = new TaskPoller({ logger: this.logger, pollInterval: opts.config.get('xpack.task_manager.poll_interval'), - work: (): Promise => + work: (): Promise => fillPool( - pool.run, + async tasks => await pool.run(tasks), () => claimAvailableTasks( this.store.claimAvailableTasks.bind(this.store), @@ -259,20 +260,44 @@ export async function claimAvailableTasks( logger: Logger ) { if (availableWorkers > 0) { - const { docs, claimedTasks } = await claim({ - size: availableWorkers, - claimOwnershipUntil: intervalFromNow('30s')!, - }); + performance.mark('claimAvailableTasks_start'); + + try { + const { docs, claimedTasks } = await claim({ + size: availableWorkers, + claimOwnershipUntil: intervalFromNow('30s')!, + }); - if (docs.length !== claimedTasks) { - logger.warn( - `[Task Ownership error]: (${claimedTasks}) tasks were claimed by Kibana, but (${docs.length}) tasks were fetched` + if (claimedTasks === 0) { + performance.mark('claimAvailableTasks.noTasks'); + } + performance.mark('claimAvailableTasks_stop'); + performance.measure( + 'claimAvailableTasks', + 'claimAvailableTasks_start', + 'claimAvailableTasks_stop' ); + + if (docs.length !== claimedTasks) { + logger.warn( + `[Task Ownership error]: (${claimedTasks}) tasks were claimed by Kibana, but (${docs.length}) tasks were fetched` + ); + } + return docs; + } catch (ex) { + if (identifyEsError(ex).includes('cannot execute [inline] scripts')) { + logger.warn( + `Task Manager cannot operate when inline scripts are disabled in Elasticsearch` + ); + } else { + throw ex; + } } - return docs; + } else { + performance.mark('claimAvailableTasks.noAvailableWorkers'); + logger.info( + `[Task Ownership]: Task Manager has skipped Claiming Ownership of available tasks at it has ran out Available Workers. If this happens often, consider adjusting the "xpack.task_manager.max_workers" configuration.` + ); } - logger.info( - `[Task Ownership]: Task Manager has skipped Claiming Ownership of available tasks at it has ran out Available Workers. If this happens often, consider adjusting the "xpack.task_manager.max_workers" configuration.` - ); return []; } diff --git a/x-pack/legacy/plugins/task_manager/task_poller.test.ts b/x-pack/legacy/plugins/task_manager/task_poller.test.ts index 478c1a4dc1b17c..88bcf29ec60849 100644 --- a/x-pack/legacy/plugins/task_manager/task_poller.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_poller.test.ts @@ -73,7 +73,9 @@ describe('TaskPoller', () => { await doneWorking; expect(count).toEqual(2); - sinon.assert.calledWithMatch(logger.error, /Dang it/i); + expect(logger.error.mock.calls[0][0]).toMatchInlineSnapshot( + `"Failed to poll for work: Error: Dang it!"` + ); }); test('is stoppable', async () => { diff --git a/x-pack/legacy/plugins/task_manager/task_poller.ts b/x-pack/legacy/plugins/task_manager/task_poller.ts index ae023f96a40647..0f7b49f17872ad 100644 --- a/x-pack/legacy/plugins/task_manager/task_poller.ts +++ b/x-pack/legacy/plugins/task_manager/task_poller.ts @@ -8,27 +8,28 @@ * This module contains the logic for polling the task manager index for new work. */ +import { performance } from 'perf_hooks'; import { Logger } from './types'; -type WorkFn = () => Promise; +type WorkFn = () => Promise; -interface Opts { +interface Opts { pollInterval: number; logger: Logger; - work: WorkFn; + work: WorkFn; } /** * Performs work on a scheduled interval, logging any errors. This waits for work to complete * (or error) prior to attempting another run. */ -export class TaskPoller { +export class TaskPoller { private isStarted = false; private isWorking = false; private timeout: any; private pollInterval: number; private logger: Logger; - private work: WorkFn; + private work: WorkFn; /** * Constructs a new TaskPoller. @@ -38,7 +39,7 @@ export class TaskPoller { * @prop {Logger} logger - The task manager logger * @prop {WorkFn} work - An empty, asynchronous function that performs the desired work */ - constructor(opts: Opts) { + constructor(opts: Opts) { this.pollInterval = opts.pollInterval; this.logger = opts.logger; this.work = opts.work; @@ -57,8 +58,16 @@ export class TaskPoller { const poll = async () => { await this.attemptWork(); + performance.mark('TaskPoller.sleep'); if (this.isStarted) { - this.timeout = setTimeout(poll, this.pollInterval); + this.timeout = setTimeout( + tryAndLogOnError(() => { + performance.mark('TaskPoller.poll'); + performance.measure('TaskPoller.sleepDuration', 'TaskPoller.sleep', 'TaskPoller.poll'); + poll(); + }, this.logger), + this.pollInterval + ); } }; @@ -94,3 +103,13 @@ export class TaskPoller { } } } + +function tryAndLogOnError(fn: Function, logger: Logger): Function { + return () => { + try { + fn(); + } catch (err) { + logger.error(`Task Poller polling phase failed: ${err}`); + } + }; +} diff --git a/x-pack/legacy/plugins/task_manager/task_pool.test.ts b/x-pack/legacy/plugins/task_manager/task_pool.test.ts index e6a83dd1911bd1..4967f4383294fc 100644 --- a/x-pack/legacy/plugins/task_manager/task_pool.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_pool.test.ts @@ -5,7 +5,7 @@ */ import sinon from 'sinon'; -import { TaskPool } from './task_pool'; +import { TaskPool, TaskPoolRunResult } from './task_pool'; import { mockLogger, resolvable, sleep } from './test_utils'; describe('TaskPool', () => { @@ -17,7 +17,7 @@ describe('TaskPool', () => { const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); - expect(result).toBeTruthy(); + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); expect(pool.occupiedWorkers).toEqual(3); }); @@ -29,7 +29,7 @@ describe('TaskPool', () => { const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); - expect(result).toBeTruthy(); + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); expect(pool.availableWorkers).toEqual(7); }); @@ -48,10 +48,74 @@ describe('TaskPool', () => { { ...mockTask(), run: shouldNotRun }, ]); - expect(result).toBeFalsy(); + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); expect(pool.availableWorkers).toEqual(0); - sinon.assert.calledTwice(shouldRun); - sinon.assert.notCalled(shouldNotRun); + expect(shouldRun).toHaveBeenCalledTimes(2); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + + test('should log when marking a Task as running fails', async () => { + const logger = mockLogger(); + const pool = new TaskPool({ + maxWorkers: 2, + logger, + }); + + const taskFailedToMarkAsRunning = mockTask(); + taskFailedToMarkAsRunning.markTaskAsRunning.mockImplementation(async () => { + throw new Error(`Mark Task as running has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToMarkAsRunning, mockTask()]); + + expect(logger.error.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to mark Task TaskType \\"shooooo\\" as running: Mark Task as running has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('should log when running a Task fails', async () => { + const logger = mockLogger(); + const pool = new TaskPool({ + maxWorkers: 3, + logger, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); + + expect(logger.warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Task TaskType \\"shooooo\\" failed in attempt to run: Run Task has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('Running a task which fails still takes up capacity', async () => { + const logger = mockLogger(); + const pool = new TaskPool({ + maxWorkers: 1, + logger, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + await sleep(0); + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([taskFailedToRun, mockTask()]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); }); test('clears up capacity when a task completes', async () => { @@ -78,7 +142,7 @@ describe('TaskPool', () => { { ...mockTask(), run: secondRun }, ]); - expect(result).toBeFalsy(); + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); expect(pool.occupiedWorkers).toEqual(1); expect(pool.availableWorkers).toEqual(0); @@ -133,7 +197,7 @@ describe('TaskPool', () => { }, ]); - expect(result).toBeTruthy(); + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); expect(pool.occupiedWorkers).toEqual(2); expect(pool.availableWorkers).toEqual(0); @@ -173,7 +237,7 @@ describe('TaskPool', () => { }, ]); - expect(result).toBeTruthy(); + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); await pool.run([]); expect(pool.occupiedWorkers).toEqual(0); @@ -181,11 +245,13 @@ describe('TaskPool', () => { // Allow the task to cancel... await cancelled; - sinon.assert.calledWithMatch(logger.error, /Failed to cancel task "shooooo!"/); + expect(logger.error.mock.calls[0][0]).toMatchInlineSnapshot( + `"Failed to cancel task \\"shooooo!\\": Error: Dern!"` + ); }); function mockRun() { - return sinon.spy(async () => { + return jest.fn(async () => { await sleep(0); return { state: {} }; }); @@ -195,8 +261,9 @@ describe('TaskPool', () => { return { isExpired: false, cancel: async () => undefined, - markTaskAsRunning: async () => true, + markTaskAsRunning: jest.fn(async () => true), run: mockRun(), + toString: () => `TaskType "shooooo"`, }; } }); diff --git a/x-pack/legacy/plugins/task_manager/task_pool.ts b/x-pack/legacy/plugins/task_manager/task_pool.ts index 7afbec65a0d8c6..5828cb0df4a4d0 100644 --- a/x-pack/legacy/plugins/task_manager/task_pool.ts +++ b/x-pack/legacy/plugins/task_manager/task_pool.ts @@ -8,7 +8,7 @@ * This module contains the logic that ensures we don't run too many * tasks at once in a given Kibana instance. */ - +import { performance } from 'perf_hooks'; import { Logger } from './types'; import { TaskRunner } from './task_runner'; @@ -17,6 +17,13 @@ interface Opts { logger: Logger; } +export enum TaskPoolRunResult { + RunningAllClaimedTasks = 'RunningAllClaimedTasks', + RanOutOfCapacity = 'RanOutOfCapacity', +} + +const VERSION_CONFLICT_MESSAGE = 'Task has been claimed by another Kibana service'; + /** * Runs tasks in batches, taking costs into account. */ @@ -66,38 +73,64 @@ export class TaskPool { }; public cancelRunningTasks() { - this.logger.debug(`Cancelling running tasks.`); + this.logger.debug('Cancelling running tasks.'); for (const task of this.running) { this.cancelTask(task); } } - private async attemptToRun(tasks: TaskRunner[]) { - for (const task of tasks) { - if (this.availableWorkers > 0) { - if (await task.markTaskAsRunning()) { - this.running.add(task); - task - .run() - .catch(err => { - this.logger.warn(`Task ${task} failed in attempt to run: ${err.message}`); - }) - .then(() => this.running.delete(task)); - } else { - this.logger.warn(`Failed to mark Task ${task} as running`); - } - } else { - return false; + private async attemptToRun(tasks: TaskRunner[]): Promise { + const [tasksToRun, leftOverTasks] = partitionListByCount(tasks, this.availableWorkers); + if (tasksToRun.length) { + performance.mark('attemptToRun_start'); + await Promise.all( + tasksToRun.map( + async task => + await task + .markTaskAsRunning() + .then((hasTaskBeenMarkAsRunning: boolean) => + hasTaskBeenMarkAsRunning + ? this.handleMarkAsRunning(task) + : this.handleFailureOfMarkAsRunning(task, { + name: 'TaskPoolVersionConflictError', + message: VERSION_CONFLICT_MESSAGE, + }) + ) + .catch(ex => this.handleFailureOfMarkAsRunning(task, ex)) + ) + ); + + performance.mark('attemptToRun_stop'); + performance.measure('taskPool.attemptToRun', 'attemptToRun_start', 'attemptToRun_stop'); + } + + if (leftOverTasks.length) { + if (this.availableWorkers) { + return this.attemptToRun(leftOverTasks); } + return TaskPoolRunResult.RanOutOfCapacity; } + return TaskPoolRunResult.RunningAllClaimedTasks; + } + + private handleMarkAsRunning(task: TaskRunner) { + this.running.add(task); + task + .run() + .catch(err => { + this.logger.warn(`Task ${task.toString()} failed in attempt to run: ${err.message}`); + }) + .then(() => this.running.delete(task)); + } - return true; + private handleFailureOfMarkAsRunning(task: TaskRunner, err: Error) { + this.logger.error(`Failed to mark Task ${task.toString()} as running: ${err.message}`); } private cancelExpiredTasks() { for (const task of this.running) { if (task.isExpired) { - this.logger.debug(`Cancelling expired task ${task}.`); + this.logger.debug(`Cancelling expired task ${task.toString()}.`); this.cancelTask(task); } } @@ -105,11 +138,16 @@ export class TaskPool { private async cancelTask(task: TaskRunner) { try { - this.logger.debug(`Cancelling task ${task}.`); + this.logger.debug(`Cancelling task ${task.toString()}.`); this.running.delete(task); await task.cancel(); } catch (err) { - this.logger.error(`Failed to cancel task ${task}: ${err}`); + this.logger.error(`Failed to cancel task ${task.toString()}: ${err}`); } } } + +function partitionListByCount(list: T[], count: number): [T[], T[]] { + const listInCount = list.splice(0, count); + return [listInCount, list]; +} diff --git a/x-pack/legacy/plugins/task_manager/task_runner.test.ts b/x-pack/legacy/plugins/task_manager/task_runner.test.ts index 49c55279eafe7a..578b86ba0b3f69 100644 --- a/x-pack/legacy/plugins/task_manager/task_runner.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_runner.test.ts @@ -10,6 +10,7 @@ import { minutesFromNow } from './lib/intervals'; import { ConcreteTaskInstance } from './task'; import { TaskManagerRunner } from './task_runner'; import { mockLogger } from './test_utils'; +import { SavedObjectsErrorHelpers } from '../../../../src/core/server/saved_objects/service/lib/errors'; let fakeTimer: sinon.SinonFakeTimers; @@ -202,7 +203,7 @@ describe('TaskManagerRunner', () => { await promise; expect(wasCancelled).toBeTruthy(); - sinon.assert.neverCalledWithMatch(logger.warn, /not cancellable/); + expect(logger.warn).not.toHaveBeenCalled(); }); test('warns if cancel is called on a non-cancellable task', async () => { @@ -220,7 +221,10 @@ describe('TaskManagerRunner', () => { await runner.cancel(); await promise; - sinon.assert.calledWithMatch(logger.warn, /not cancellable/); + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"The task bar \\"foo\\" is not cancellable."` + ); }); test('sets startedAt, status, attempts and retryAt when claiming a task', async () => { @@ -420,6 +424,74 @@ describe('TaskManagerRunner', () => { ); }); + test('it returns false when markTaskAsRunning fails due to VERSION_CONFLICT_STATUS', async () => { + const id = _.random(1, 20).toString(); + const initialAttempts = _.random(1, 3); + const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); + const timeoutMinutes = 1; + const getRetryStub = sinon.stub().returns(nextRetry); + const { runner, store } = testOpts({ + instance: { + id, + attempts: initialAttempts, + interval: undefined, + }, + definitions: { + bar: { + timeout: `${timeoutMinutes}m`, + getRetry: getRetryStub, + createTaskRunner: () => ({ + run: async () => undefined, + }), + }, + }, + }); + + store.update = sinon + .stub() + .throws( + SavedObjectsErrorHelpers.decorateConflictError(new Error('repo error')).output.payload + ); + + expect(await runner.markTaskAsRunning()).toEqual(false); + }); + + test('it throw when markTaskAsRunning fails for unexpected reasons', async () => { + const id = _.random(1, 20).toString(); + const initialAttempts = _.random(1, 3); + const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); + const timeoutMinutes = 1; + const getRetryStub = sinon.stub().returns(nextRetry); + const { runner, store } = testOpts({ + instance: { + id, + attempts: initialAttempts, + interval: undefined, + }, + definitions: { + bar: { + timeout: `${timeoutMinutes}m`, + getRetry: getRetryStub, + createTaskRunner: () => ({ + run: async () => undefined, + }), + }, + }, + }); + + store.update = sinon + .stub() + .throws(SavedObjectsErrorHelpers.createGenericNotFoundError('type', 'id').output.payload); + + return expect(runner.markTaskAsRunning()).rejects.toMatchInlineSnapshot(` + Object { + "error": "Not Found", + "message": "Saved object [type/id] not found", + "statusCode": 404, + } + `); + }); + test('uses getRetry (returning true) to set retryAt when defined', async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); @@ -601,6 +673,7 @@ describe('TaskManagerRunner', () => { }; const runner = new TaskManagerRunner({ beforeRun: context => Promise.resolve(context), + beforeMarkRunning: context => Promise.resolve(context), logger, store, instance: Object.assign( @@ -655,9 +728,10 @@ describe('TaskManagerRunner', () => { await runner.run(); if (shouldBeValid) { - sinon.assert.notCalled(logger.warn); + expect(logger.warn).not.toHaveBeenCalled(); } else { - sinon.assert.calledWith(logger.warn, sinon.match(/invalid task result/i)); + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn.mock.calls[0][0]).toMatch(/invalid task result/i); } } diff --git a/x-pack/legacy/plugins/task_manager/task_runner.ts b/x-pack/legacy/plugins/task_manager/task_runner.ts index a51e0c885b978d..9d1431ed004e32 100644 --- a/x-pack/legacy/plugins/task_manager/task_runner.ts +++ b/x-pack/legacy/plugins/task_manager/task_runner.ts @@ -10,10 +10,11 @@ * rescheduling, middleware application, etc. */ +import { performance } from 'perf_hooks'; import Joi from 'joi'; import { intervalFromDate, intervalFromNow } from './lib/intervals'; import { Logger } from './types'; -import { BeforeRunFunction } from './lib/middleware'; +import { BeforeRunFunction, BeforeMarkRunningFunction } from './lib/middleware'; import { CancelFunction, CancellableTask, @@ -32,7 +33,7 @@ export interface TaskRunner { cancel: CancelFunction; markTaskAsRunning: () => Promise; run: () => Promise; - toString?: () => string; + toString: () => string; } interface Updatable { @@ -47,6 +48,7 @@ interface Opts { instance: ConcreteTaskInstance; store: Updatable; beforeRun: BeforeRunFunction; + beforeMarkRunning: BeforeMarkRunningFunction; } /** @@ -62,8 +64,9 @@ export class TaskManagerRunner implements TaskRunner { private instance: ConcreteTaskInstance; private definitions: TaskDictionary; private logger: Logger; - private store: Updatable; + private bufferedTaskStore: Updatable; private beforeRun: BeforeRunFunction; + private beforeMarkRunning: BeforeMarkRunningFunction; /** * Creates an instance of TaskManagerRunner. @@ -79,8 +82,9 @@ export class TaskManagerRunner implements TaskRunner { this.instance = sanitizeInstance(opts.instance); this.definitions = opts.definitions; this.logger = opts.logger; - this.store = opts.store; + this.bufferedTaskStore = opts.store; this.beforeRun = opts.beforeRun; + this.beforeMarkRunning = opts.beforeMarkRunning; } /** @@ -153,14 +157,20 @@ export class TaskManagerRunner implements TaskRunner { * @returns {Promise} */ public async markTaskAsRunning(): Promise { + performance.mark('markTaskAsRunning_start'); + const VERSION_CONFLICT_STATUS = 409; - const attempts = this.instance.attempts + 1; const now = new Date(); - const ownershipClaimedUntil = this.instance.retryAt; + const { taskInstance } = await this.beforeMarkRunning({ + taskInstance: this.instance, + }); + + const attempts = taskInstance.attempts + 1; + const ownershipClaimedUntil = taskInstance.retryAt; try { - const { id } = this.instance; + const { id } = taskInstance; const timeUntilClaimExpires = howManyMsUntilOwnershipClaimExpires(ownershipClaimedUntil); if (timeUntilClaimExpires < 0) { @@ -171,8 +181,8 @@ export class TaskManagerRunner implements TaskRunner { ); } - this.instance = await this.store.update({ - ...this.instance, + this.instance = await this.bufferedTaskStore.update({ + ...taskInstance, status: 'running', startedAt: now, attempts, @@ -198,13 +208,14 @@ export class TaskManagerRunner implements TaskRunner { ); } + performanceStopMarkingTaskAsRunning(); return true; } catch (error) { + performanceStopMarkingTaskAsRunning(); if (error.statusCode !== VERSION_CONFLICT_STATUS) { throw error; } } - return false; } @@ -263,7 +274,7 @@ export class TaskManagerRunner implements TaskRunner { runAt = intervalFromDate(startedAt, this.instance.interval)!; } - await this.store.update({ + await this.bufferedTaskStore.update({ ...this.instance, runAt, state, @@ -280,7 +291,7 @@ export class TaskManagerRunner implements TaskRunner { private async processResultWhenDone(result: RunResult): Promise { // not a recurring task: clean up by removing the task instance from store try { - await this.store.remove(this.instance.id); + await this.bufferedTaskStore.remove(this.instance.id); } catch (err) { if (err.statusCode === 404) { this.logger.warn(`Task cleanup of ${this} failed in processing. Was remove called twice?`); @@ -306,7 +317,7 @@ export class TaskManagerRunner implements TaskRunner { return 'idle'; } - const maxAttempts = this.definition.maxAttempts || this.store.maxAttempts; + const maxAttempts = this.definition.maxAttempts || this.bufferedTaskStore.maxAttempts; return this.instance.attempts < maxAttempts ? 'idle' : 'failed'; } @@ -352,3 +363,12 @@ function sanitizeInstance(instance: ConcreteTaskInstance): ConcreteTaskInstance function howManyMsUntilOwnershipClaimExpires(ownershipClaimedUntil: Date | null): number { return ownershipClaimedUntil ? ownershipClaimedUntil.getTime() - Date.now() : 0; } + +function performanceStopMarkingTaskAsRunning() { + performance.mark('markTaskAsRunning_stop'); + performance.measure( + 'taskRunner.markTaskAsRunning', + 'markTaskAsRunning_start', + 'markTaskAsRunning_stop' + ); +} diff --git a/x-pack/legacy/plugins/task_manager/task_store.test.ts b/x-pack/legacy/plugins/task_manager/task_store.test.ts index 9779dc5efd28bb..46efc4bb57ba76 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.test.ts @@ -9,7 +9,6 @@ import sinon from 'sinon'; import uuid from 'uuid'; import { TaskDictionary, TaskDefinition, TaskInstance, TaskStatus } from './task'; import { FetchOpts, StoreOpts, OwnershipClaimingOpts, TaskStore } from './task_store'; -import { mockLogger } from './test_utils'; import { savedObjectsClientMock } from 'src/core/server/mocks'; import { SavedObjectsSerializer, SavedObjectsSchema, SavedObjectAttributes } from 'src/core/server'; @@ -77,6 +76,7 @@ describe('TaskStore', () => { test('serializes the params and state', async () => { const task = { + id: 'id', params: { hello: 'world' }, state: { foo: 'bar' }, taskType: 'report', @@ -99,7 +99,10 @@ describe('TaskStore', () => { taskType: 'report', user: undefined, }, - {} + { + id: 'id', + refresh: false, + } ); expect(result).toEqual({ @@ -326,233 +329,6 @@ describe('TaskStore', () => { }); }); - describe('fetchAvailableTasks', () => { - async function testFetchAvailableTasks({ opts = {}, hits = [] }: any = {}) { - const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits } })); - const store = new TaskStore({ - callCluster, - logger: mockLogger(), - definitions: taskDefinitions, - maxAttempts: 2, - serializer, - ...opts, - }); - - const result = await store.fetchAvailableTasks(); - - sinon.assert.calledOnce(callCluster); - sinon.assert.calledWith(callCluster, 'search'); - - return { - result, - args: callCluster.args[0][1], - }; - } - - test('it returns normally with no tasks when the index does not exist.', async () => { - const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits: [] } })); - const store = new TaskStore({ - index: 'tasky', - taskManagerId: '', - serializer, - callCluster, - definitions: taskDefinitions, - maxAttempts: 2, - savedObjectsRepository: savedObjectsClient, - }); - - const result = await store.fetchAvailableTasks(); - - sinon.assert.calledOnce(callCluster); - sinon.assert.calledWithMatch(callCluster, 'search', { ignoreUnavailable: true }); - - expect(result.length).toBe(0); - }); - - test('it filters tasks by supported types, maxAttempts, and runAt', async () => { - const maxAttempts = _.random(2, 43); - const customMaxAttempts = _.random(44, 100); - const { args } = await testFetchAvailableTasks({ - opts: { - maxAttempts, - definitions: { - foo: { - type: 'foo', - title: '', - createTaskRunner: jest.fn(), - }, - bar: { - type: 'bar', - title: '', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - }, - }, - }); - expect(args).toMatchObject({ - body: { - query: { - bool: { - must: [ - { term: { type: 'task' } }, - { - bool: { - must: [ - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { term: { 'task.status': 'running' } }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - { - bool: { - should: [ - { exists: { field: 'task.interval' } }, - { - bool: { - must: [ - { term: { 'task.taskType': 'foo' } }, - { - range: { - 'task.attempts': { - lt: maxAttempts, - }, - }, - }, - ], - }, - }, - { - bool: { - must: [ - { term: { 'task.taskType': 'bar' } }, - { - range: { - 'task.attempts': { - lt: customMaxAttempts, - }, - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - size: 10, - sort: { - _script: { - type: 'number', - order: 'asc', - script: { - lang: 'expression', - source: `doc['task.retryAt'].value || doc['task.runAt'].value`, - }, - }, - }, - seq_no_primary_term: true, - }, - }); - }); - - test('it returns task objects', async () => { - const runAt = new Date(); - const { result } = await testFetchAvailableTasks({ - hits: [ - { - _id: 'aaa', - _source: { - type: 'task', - task: { - runAt, - taskType: 'foo', - interval: undefined, - attempts: 0, - status: 'idle', - params: '{ "hello": "world" }', - state: '{ "baby": "Henhen" }', - user: 'jimbo', - scope: ['reporting'], - }, - }, - _seq_no: 1, - _primary_term: 2, - sort: ['a', 1], - }, - { - _id: 'bbb', - _source: { - type: 'task', - task: { - runAt, - taskType: 'bar', - interval: '5m', - attempts: 2, - status: 'running', - params: '{ "shazm": 1 }', - state: '{ "henry": "The 8th" }', - user: 'dabo', - scope: ['reporting', 'ceo'], - }, - }, - _seq_no: 3, - _primary_term: 4, - sort: ['b', 2], - }, - ], - }); - expect(result).toMatchObject([ - { - attempts: 0, - id: 'aaa', - interval: undefined, - params: { hello: 'world' }, - runAt, - scope: ['reporting'], - state: { baby: 'Henhen' }, - status: 'idle', - taskType: 'foo', - user: 'jimbo', - }, - { - attempts: 2, - id: 'bbb', - interval: '5m', - params: { shazm: 1 }, - runAt, - scope: ['reporting', 'ceo'], - state: { henry: 'The 8th' }, - status: 'running', - taskType: 'bar', - user: 'dabo', - }, - ]); - }); - }); - describe('claimAvailableTasks', () => { async function testClaimAvailableTasks({ opts = {}, @@ -927,7 +703,7 @@ describe('TaskStore', () => { user: undefined, ownerId: null, }, - { version: '123' } + { version: '123', refresh: false } ); expect(result).toEqual({ diff --git a/x-pack/legacy/plugins/task_manager/task_store.ts b/x-pack/legacy/plugins/task_manager/task_store.ts index 9b5253d8be7f95..58bffd2269eb6f 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.ts @@ -70,6 +70,11 @@ export interface ClaimOwnershipResult { docs: ConcreteTaskInstance[]; } +export interface BulkUpdateTaskFailureResult { + error: NonNullable; + task: ConcreteTaskInstance; +} + export interface UpdateByQueryResult { updated: number; version_conflicts: number; @@ -107,8 +112,6 @@ export class TaskStore { this.definitions = opts.definitions; this.serializer = opts.serializer; this.savedObjectsRepository = opts.savedObjectsRepository; - - this.fetchAvailableTasks = this.fetchAvailableTasks.bind(this); } /** @@ -128,7 +131,7 @@ export class TaskStore { const savedObject = await this.savedObjectsRepository.create( 'task', taskInstanceToAttributes(taskInstance), - { id: taskInstance.id } + { id: taskInstance.id, refresh: false } ); return savedObjectToConcreteTaskInstance(savedObject); @@ -148,88 +151,6 @@ export class TaskStore { }); } - /** - * Fetches tasks from the index, which are ready to be run. - * - runAt is now or past - * - id is not currently running in this instance of Kibana - * - has a type that is in our task definitions - * - * @param {TaskQuery} query - * @prop {string[]} types - Task types to be queried - * @prop {number} size - The number of task instances to retrieve - * @returns {Promise} - */ - public async fetchAvailableTasks(): Promise { - const { docs } = await this.search({ - query: { - bool: { - must: [ - // Either a task with idle status and runAt <= now or - // status running with a retryAt <= now. - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { term: { 'task.status': 'running' } }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - // Either task has an interval or the attempts < the maximum configured - { - bool: { - should: [ - { exists: { field: 'task.interval' } }, - ...Object.entries(this.definitions).map(([type, definition]) => ({ - bool: { - must: [ - { term: { 'task.taskType': type } }, - { - range: { - 'task.attempts': { - lt: definition.maxAttempts || this.maxAttempts, - }, - }, - }, - ], - }, - })), - ], - }, - }, - ], - }, - }, - size: 10, - sort: { - _script: { - type: 'number', - order: 'asc', - script: { - lang: 'expression', - source: `doc['task.retryAt'].value || doc['task.runAt'].value`, - }, - }, - }, - seq_no_primary_term: true, - }); - - return docs; - } - /** * Claims available tasks from the index, which are ready to be run. * - runAt is now or past @@ -376,6 +297,7 @@ export class TaskStore { return docs; } + /** * Updates the specified doc in the index, returning the doc * with its version up to date. @@ -388,7 +310,10 @@ export class TaskStore { 'task', doc.id, taskInstanceToAttributes(doc), - { version: doc.version } + { + refresh: false, + version: doc.version, + } ); return savedObjectToConcreteTaskInstance(updatedSavedObject); diff --git a/x-pack/legacy/plugins/task_manager/test_utils/index.ts b/x-pack/legacy/plugins/task_manager/test_utils/index.ts index 8ee5cdce064cc2..719ccadbe33dde 100644 --- a/x-pack/legacy/plugins/task_manager/test_utils/index.ts +++ b/x-pack/legacy/plugins/task_manager/test_utils/index.ts @@ -8,8 +8,6 @@ * A handful of helper functions for testing the task manager. */ -import sinon from 'sinon'; - // Caching this here to avoid setTimeout mocking affecting our tests. const nativeTimeout = setTimeout; @@ -18,10 +16,10 @@ const nativeTimeout = setTimeout; */ export function mockLogger() { return { - info: sinon.stub(), - debug: sinon.stub(), - warn: sinon.stub(), - error: sinon.stub(), + info: jest.fn(), + debug: jest.fn(), + warn: jest.fn(), + error: jest.fn(), }; } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap index d915bcb72e05eb..511485e70d0dc5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap @@ -1,82 +1,81 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MonitorCharts component renders the component without errors 1`] = ` - - - - - - - + + + + + - - - + } + /> + + `; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/ping_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/ping_list.test.tsx.snap index 165cfe5c370d1e..9c6d2e2f495c11 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/ping_list.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/ping_list.test.tsx.snap @@ -43,41 +43,30 @@ exports[`PingList component renders sorted list without errors 1`] = ` label="Status" labelType="label" > - @@ -92,37 +81,26 @@ exports[`PingList component renders sorted list without errors 1`] = ` label="Location" labelType="label" > - diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap index d6b96fef9b2ee0..ebbd8a4ac56a83 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap @@ -4,22 +4,10 @@ exports[`Snapshot component renders without errors 1`] = ` - -

    - -

    -
    + diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot_heading.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot_heading.test.tsx.snap new file mode 100644 index 00000000000000..da2e39c3dc1272 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot_heading.test.tsx.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SnapshotHeading renders custom heading for no down monitors 1`] = ` + +

    + All monitors are up +

    +
    +`; + +exports[`SnapshotHeading renders custom heading for no monitors 1`] = ` + +

    + No monitors found +

    +
    +`; + +exports[`SnapshotHeading renders standard heading for valid counts 1`] = ` + +

    + 3/17 monitors are down +

    +
    +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/ping_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/ping_list.test.tsx index 64aed656158469..46da7e52333543 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/ping_list.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/ping_list.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { PingResults, Ping } from '../../../../common/graphql/types'; -import { PingListComponent, BaseLocationOptions, toggleDetails } from '../ping_list'; +import { PingListComponent, AllLocationOption, toggleDetails } from '../ping_list'; import { EuiComboBoxOptionProps } from '@elastic/eui'; import { ExpandedRowMap } from '../monitor_list/types'; @@ -210,7 +210,7 @@ describe('PingList component', () => { onUpdateApp={jest.fn()} pageSize={30} selectedOption="down" - selectedLocation={BaseLocationOptions} + selectedLocation={AllLocationOption.value} /> ); expect(component).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx new file mode 100644 index 00000000000000..5ddef3d0aabd10 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { SnapshotHeading } from '../snapshot_heading'; + +describe('SnapshotHeading', () => { + it('renders custom heading for no down monitors', () => { + const wrapper = shallowWithIntl(); + expect(wrapper).toMatchSnapshot(); + }); + + it('renders standard heading for valid counts', () => { + const wrapper = shallowWithIntl(); + expect(wrapper).toMatchSnapshot(); + }); + + it('renders custom heading for no monitors', () => { + const wrapper = shallowWithIntl(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/snapshot_histogram.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/snapshot_histogram.test.tsx index bad6c5e2a2ba1d..db78c063b7ed58 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/snapshot_histogram.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/snapshot_histogram.test.tsx @@ -12,6 +12,7 @@ describe('SnapshotHistogram component', () => { const props: SnapshotHistogramProps = { absoluteStartDate: 1548697920000, absoluteEndDate: 1548700920000, + isResponsive: false, }; it('renders the component without errors', () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx index a5d2d9cfc27f29..37edd208712456 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx @@ -19,27 +19,16 @@ import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; -import styled from 'styled-components'; import { getColorsMap } from './get_colors_map'; import { getChartDateLabel } from '../../../lib/helper'; import { withUptimeGraphQL, UptimeGraphQLQueryProps } from '../../higher_order'; import { snapshotHistogramQuery } from '../../../queries/snapshot_histogram_query'; import { ChartWrapper } from './chart_wrapper'; import { UptimeSettingsContext } from '../../../contexts'; +import { ResponsiveWrapperProps, withResponsiveWrapper } from '../../higher_order'; import { HistogramResult } from '../../../../common/domain_types'; -const SnapshotHistogramWrapper = styled.div` - margin-left: 120px; - @media (max-width: 950px) { - margin-left: 48px; - } - @media (max-width: 767px) { - margin-left: 12px; - margin-top: 40px; - } -`; - -export interface SnapshotHistogramProps { +interface HistogramProps { /** * The date/time for the start of the timespan. */ @@ -55,13 +44,17 @@ export interface SnapshotHistogramProps { height?: string; } +export type SnapshotHistogramProps = HistogramProps & ResponsiveWrapperProps; + interface SnapshotHistogramQueryResult { queryResult?: HistogramResult; } -type Props = UptimeGraphQLQueryProps & SnapshotHistogramProps; +type Props = UptimeGraphQLQueryProps & + SnapshotHistogramProps & + ResponsiveWrapperProps; -export const SnapshotHistogramComponent = ({ +export const SnapshotHistogramComponent: React.FC = ({ absoluteStartDate, absoluteEndDate, data, @@ -125,7 +118,7 @@ export const SnapshotHistogramComponent = ({ }); const upSpecId = getSpecId(upMonitorsId); return ( - + <>

    - + ); }; export const SnapshotHistogram = withUptimeGraphQL< SnapshotHistogramQueryResult, SnapshotHistogramProps ->(SnapshotHistogramComponent, snapshotHistogramQuery); +>(withResponsiveWrapper(SnapshotHistogramComponent), snapshotHistogramQuery); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap index 22eacdb91c0dc2..28ac27054b8566 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap @@ -3,6 +3,7 @@ exports[`FilterStatusButton renders without errors for valid props 1`] = ` { beforeEach(() => { props = { content: 'Up', + dataTestSubj: 'foo', value: 'up', withNext: true, }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx index cfdb27d9c41224..f27514bf76a11a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx @@ -105,6 +105,7 @@ export const FilterGroupComponent = ({ content={i18n.translate('xpack.uptime.filterBar.filterUpLabel', { defaultMessage: 'Up', })} + dataTestSubj="xpack.uptime.filterBar.filterStatusUp" value="up" withNext={true} /> @@ -112,6 +113,7 @@ export const FilterGroupComponent = ({ content={i18n.translate('xpack.uptime.filterBar.filterDownLabel', { defaultMessage: 'Down', })} + dataTestSubj="xpack.uptime.filterBar.filterStatusDown" value="down" withNext={false} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx index 6f2659ddbc83e9..95f4c30337d627 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx @@ -10,15 +10,22 @@ import { useUrlParams } from '../../../hooks'; export interface FilterStatusButtonProps { content: string; + dataTestSubj: string; value: string; withNext: boolean; } -export const FilterStatusButton = ({ content, value, withNext }: FilterStatusButtonProps) => { +export const FilterStatusButton = ({ + content, + dataTestSubj, + value, + withNext, +}: FilterStatusButtonProps) => { const [getUrlParams, setUrlParams] = useUrlParams(); const { statusFilter: urlValue } = getUrlParams(); return ( { const nextFilter = { statusFilter: urlValue === value ? '' : value, pagination: '' }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx index 51c58a871d0e7a..7d8e788f49ea00 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx @@ -48,26 +48,25 @@ export const MonitorChartsComponent = ({ const { absoluteDateRangeStart, absoluteDateRangeEnd } = getUrlParams(); return ( - - - - - - - - - - + + + + + + + + ); } return ( diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap index e53d73fb5a4613..c2167993b80b93 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap @@ -88,6 +88,7 @@ exports[`MonitorList component renders a no items message when no data is provid grow={false} > @@ -95,6 +96,7 @@ exports[`MonitorList component renders a no items message when no data is provid grow={false} > @@ -253,6 +255,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` grow={false} > @@ -260,6 +263,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` grow={false} > diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap index cd03c6aad5611b..619cce88b309ba 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_pagination.test.tsx.snap @@ -88,6 +88,7 @@ exports[`MonitorList component renders a no items message when no data is provid grow={false} > @@ -95,6 +96,7 @@ exports[`MonitorList component renders a no items message when no data is provid grow={false} > @@ -253,6 +255,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` grow={false} > @@ -261,6 +264,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` grow={false} > diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx index cf3a0ef4e37e75..5774453ff67ab5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx @@ -248,10 +248,18 @@ export const MonitorListComponent = (props: Props) => { - + - + diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_link.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_link.tsx index 9153b6018ce729..5b0ad2ce585370 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_link.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_link.tsx @@ -10,13 +10,15 @@ import { i18n } from '@kbn/i18n'; import { useUrlParams } from '../../hooks'; interface OverviewPageLinkProps { - pagination: string; + dataTestSubj: string; direction: string; + pagination: string; } export const OverviewPageLink: FunctionComponent = ({ - pagination, + dataTestSubj, direction, + pagination, }) => { const [, updateUrlParams] = useUrlParams(); const icon = direction === 'prev' ? 'arrowLeft' : 'arrowRight'; @@ -34,6 +36,7 @@ export const OverviewPageLink: FunctionComponent = ({ return !!pagination ? ( { updateUrlParams({ pagination }); }} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list.tsx index 5b280c5aceb87f..fb7c0a5af1e7f8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/ping_list.tsx @@ -6,12 +6,11 @@ import { EuiBadge, EuiBasicTable, - EuiComboBox, - EuiComboBoxOptionProps, EuiFlexGroup, EuiFlexItem, EuiHealth, EuiPanel, + EuiSelect, EuiSpacer, EuiText, EuiTitle, @@ -38,13 +37,13 @@ interface PingListQueryResult { } interface PingListProps { - onSelectedStatusChange: (status: string | null) => void; - onSelectedLocationChange: (location: EuiComboBoxOptionProps[]) => void; + onSelectedStatusChange: (status: string | undefined) => void; + onSelectedLocationChange: (location: any) => void; onPageCountChange: (itemCount: number) => void; onUpdateApp: () => void; pageSize: number; selectedOption: string; - selectedLocation: EuiComboBoxOptionProps[]; + selectedLocation: string | undefined; } type Props = UptimeGraphQLQueryProps & PingListProps; @@ -52,7 +51,7 @@ interface ExpandedRowMap { [key: string]: JSX.Element; } -export const BaseLocationOptions = [{ label: 'All', value: 'All' }]; +export const AllLocationOption = { text: 'All', value: '' }; export const toggleDetails = ( ping: Ping, @@ -84,32 +83,32 @@ export const PingListComponent = ({ }: Props) => { const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState({}); - const statusOptions: EuiComboBoxOptionProps[] = [ + const statusOptions = [ { - label: i18n.translate('xpack.uptime.pingList.statusOptions.allStatusOptionLabel', { + text: i18n.translate('xpack.uptime.pingList.statusOptions.allStatusOptionLabel', { defaultMessage: 'All', }), value: '', }, { - label: i18n.translate('xpack.uptime.pingList.statusOptions.upStatusOptionLabel', { + text: i18n.translate('xpack.uptime.pingList.statusOptions.upStatusOptionLabel', { defaultMessage: 'Up', }), value: 'up', }, { - label: i18n.translate('xpack.uptime.pingList.statusOptions.downStatusOptionLabel', { + text: i18n.translate('xpack.uptime.pingList.statusOptions.downStatusOptionLabel', { defaultMessage: 'Down', }), value: 'down', }, ]; const locations = get(data, 'allPings.locations'); - const locationOptions: EuiComboBoxOptionProps[] = !locations - ? BaseLocationOptions - : BaseLocationOptions.concat( + const locationOptions = !locations + ? [AllLocationOption] + : [AllLocationOption].concat( locations.map(name => { - return { label: name, value: name }; + return { text: name, value: name }; }) ); @@ -253,22 +252,18 @@ export const PingListComponent = ({ defaultMessage: 'Status', })} > - value === selectedOption) || - statusOptions[2], - ]} + { - if (typeof selectedOptions[0].value === 'string') { + value={selectedOption} + onChange={selected => { + if (typeof selected.target.value === 'string') { onSelectedStatusChange( - // @ts-ignore it's definitely a string - selectedOptions[0].value !== '' ? selectedOptions[0].value : null + selected.target && selected.target.value !== '' + ? selected.target.value + : undefined ); } }} @@ -282,16 +277,18 @@ export const PingListComponent = ({ defaultMessage: 'Location', })} > - { - onSelectedLocationChange(selectedOptions); + onChange={selected => { + onSelectedLocationChange( + selected.target && selected.target.value !== '' + ? selected.target.value + : null + ); }} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/snapshot.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/snapshot.tsx index bfc8cb70038681..8d89e53a41a45d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/snapshot.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/snapshot.tsx @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiSpacer, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { get } from 'lodash'; import { DonutChart } from './charts'; @@ -13,6 +12,7 @@ import { Snapshot as SnapshotType } from '../../../common/graphql/types'; import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../higher_order'; import { snapshotQuery } from '../../queries'; import { ChartWrapper } from './charts/chart_wrapper'; +import { SnapshotHeading } from './snapshot_heading'; const SNAPSHOT_CHART_WIDTH = 144; const SNAPSHOT_CHART_HEIGHT = 144; @@ -31,18 +31,10 @@ export const SnapshotComponent = ({ loading, }: UptimeGraphQLQueryProps) => ( - -

    - (data, 'snapshot.counts.down', 0), - total: get(data, 'snapshot.counts.total', 0), - }} - /> -

    -
    + (data, 'snapshot.counts.down', 0)} + total={get(data, 'snapshot.counts.total', 0)} + /> (data, 'snapshot.counts.up', 0)} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/snapshot_heading.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/snapshot_heading.tsx new file mode 100644 index 00000000000000..85d1294d4b0640 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/snapshot_heading.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +interface Props { + down: number; + total: number; +} + +const getMessage = (down: number, total: number): string => { + if (down === 0 && total > 0) { + return i18n.translate('xpack.uptime.snapshot.zeroDownMessage', { + defaultMessage: 'All monitors are up', + }); + } else if (down === 0 && total === 0) { + return i18n.translate('xpack.uptime.snapshot.noMonitorMessage', { + defaultMessage: 'No monitors found', + }); + } + return i18n.translate('xpack.uptime.snapshot.downCountsMessage', { + defaultMessage: '{down}/{total} monitors are down', + values: { + down, + total, + }, + }); +}; + +export const SnapshotHeading = ({ down, total }: Props) => ( + +

    {getMessage(down, total)}

    +
    +); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx index 9f1e4648b072d4..e941c2dad87d2d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx @@ -8,19 +8,16 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React from 'react'; import { SnapshotHistogram } from './charts'; import { Snapshot } from './snapshot'; -import { UptimeAppColors } from '../../uptime_app'; interface StatusPanelProps { absoluteDateRangeStart: number; absoluteDateRangeEnd: number; - colors: UptimeAppColors; sharedProps: { [key: string]: any }; } export const StatusPanel = ({ absoluteDateRangeStart, absoluteDateRangeEnd, - colors: { danger, success }, sharedProps, }: StatusPanelProps) => ( @@ -32,8 +29,9 @@ export const StatusPanel = ({ diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap new file mode 100644 index 00000000000000..65b6d7cc39e558 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap @@ -0,0 +1,221 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ResponsiveWrapper HOC is not responsive when prop is false 1`] = ` + + + +`; + +exports[`ResponsiveWrapper HOC renders a responsive wrapper 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/responsive_wrapper.test.tsx b/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/responsive_wrapper.test.tsx new file mode 100644 index 00000000000000..ae265ef1bb0c62 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/responsive_wrapper.test.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { withResponsiveWrapper } from '../responsive_wrapper'; + +interface Prop { + isResponsive: boolean; +} + +describe('ResponsiveWrapper HOC', () => { + let WrappedByHOC: React.FC; + beforeEach(() => { + WrappedByHOC = withResponsiveWrapper(() =>
    Should be responsive
    ); + }); + + it('renders a responsive wrapper', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); + + it('is not responsive when prop is false', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts b/x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts index c5b712e0ae6744..e0e14456cfc684 100644 --- a/x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts @@ -5,3 +5,4 @@ */ export { UptimeGraphQLQueryProps, withUptimeGraphQL } from './uptime_graphql_query'; +export { ResponsiveWrapperProps, withResponsiveWrapper } from './responsive_wrapper'; diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/responsive_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/higher_order/responsive_wrapper.tsx new file mode 100644 index 00000000000000..f383600ab8aa1f --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/higher_order/responsive_wrapper.tsx @@ -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 { EuiPanel } from '@elastic/eui'; +import React, { FC } from 'react'; +import styled from 'styled-components'; + +const ResponsiveWrapper = styled.div` + margin-left: 120px; + @media (max-width: 950px) { + margin-left: 48px; + } + @media (max-width: 767px) { + margin-left: 12px; + margin-top: 40px; + } +`; + +export interface ResponsiveWrapperProps { + isResponsive: boolean; +} + +/** + * HOC that wraps a component in either a responsive div or an EuiPanel. + * @param Component The component to wrap. + */ +export const withResponsiveWrapper =

    ( + Component: FC

    +): FC => ({ isResponsive, ...rest }: ResponsiveWrapperProps) => + isResponsive ? ( + + + + ) : ( + + + + ); diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 0ebcf48cd249de..86a0a9e4b0f0b9 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -7,7 +7,6 @@ import { // @ts-ignore No typings for EuiSpacer EuiSpacer, - EuiComboBoxOptionProps, } from '@elastic/eui'; import { ApolloQueryResult, OperationVariables, QueryOptions } from 'apollo-client'; import gql from 'graphql-tag'; @@ -23,7 +22,6 @@ import { UMUpdateBreadcrumbs } from '../lib/lib'; import { UptimeSettingsContext } from '../contexts'; import { useUrlParams } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; -import { BaseLocationOptions } from '../components/functional/ping_list'; import { useTrackPageview } from '../../../infra/public'; import { getTitle } from '../lib/helper/get_title'; @@ -74,16 +72,12 @@ export const MonitorPage = ({ }); }, [params]); - const [selectedLocation, setSelectedLocation] = useState( - BaseLocationOptions - ); - - const selLocationVal = selectedLocation[0].value === 'All' ? null : selectedLocation[0].value; + const [selectedLocation, setSelectedLocation] = useState(undefined); const sharedVariables = { dateRangeStart, dateRangeEnd, - location: selLocationVal, + location: selectedLocation, monitorId, }; @@ -111,7 +105,7 @@ export const MonitorPage = ({ + onSelectedStatusChange={(selectedStatus: string | undefined) => updateUrlParams({ selectedPingStatus: selectedStatus || '' }) } onUpdateApp={refreshApp} diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 445ebcb5547b88..ded16c3f8eb2f0 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -151,7 +151,6 @@ export const OverviewPage = ({ diff --git a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts index 5c481cd147c615..7b3c72f535401a 100644 --- a/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts +++ b/x-pack/legacy/plugins/uptime/server/lib/adapters/pings/__tests__/elasticsearch_pings_adapter.test.ts @@ -7,6 +7,7 @@ import { set } from 'lodash'; import { DatabaseAdapter } from '../../database'; import { ElasticsearchPingsAdapter } from '../elasticsearch_pings_adapter'; +import { assertCloseTo } from '../../../helper'; describe('ElasticsearchPingsAdapter class', () => { let database: DatabaseAdapter; @@ -115,6 +116,8 @@ describe('ElasticsearchPingsAdapter class', () => { }; const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); const result = await pingAdapter.getPingHistogram(serverRequest, 'now-15m', 'now', null); + assertCloseTo(result.interval, 36000, 100); + result.interval = 36000; expect(pingDatabase.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); @@ -132,6 +135,8 @@ describe('ElasticsearchPingsAdapter class', () => { }; const pingAdapter = new ElasticsearchPingsAdapter(pingDatabase); const result = await pingAdapter.getPingHistogram(serverRequest, 'now-15m', 'now', null); + assertCloseTo(result.interval, 36000, 100); + result.interval = 36000; expect(pingDatabase.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); @@ -197,6 +202,8 @@ describe('ElasticsearchPingsAdapter class', () => { undefined, 'down' ); + assertCloseTo(result.interval, 5609564928000, 1000); + result.interval = 5609564928000; expect(pingDatabase.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); @@ -254,6 +261,8 @@ describe('ElasticsearchPingsAdapter class', () => { searchFilter ); + assertCloseTo(result.interval, 36000, 100); + result.interval = 36000; expect(pingDatabase.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); }); @@ -276,6 +285,8 @@ describe('ElasticsearchPingsAdapter class', () => { undefined, 'down' ); + assertCloseTo(result.interval, 5609564928000, 1000); + result.interval = 5609564928000; expect(pingDatabase.search).toHaveBeenCalledTimes(1); expect(result).toMatchSnapshot(); diff --git a/x-pack/package.json b/x-pack/package.json index 1daed1951834b8..2d6a6e1155c821 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -233,7 +233,7 @@ "dedent": "^0.7.0", "del": "^5.1.0", "dragselect": "1.13.1", - "elasticsearch": "^16.4.0", + "elasticsearch": "^16.5.0", "extract-zip": "1.6.7", "file-saver": "^1.3.8", "file-type": "^10.9.0", @@ -334,7 +334,7 @@ "reselect": "3.0.1", "resize-observer-polyfill": "^1.5.0", "rison-node": "0.3.1", - "rxjs": "^6.2.1", + "rxjs": "^6.5.3", "semver": "5.7.0", "squel": "^5.13.0", "stats-lite": "^2.2.0", diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/README.md b/x-pack/plugins/encrypted_saved_objects/README.md similarity index 71% rename from x-pack/legacy/plugins/encrypted_saved_objects/README.md rename to x-pack/plugins/encrypted_saved_objects/README.md index f63c558d323e8f..a352989870079e 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/README.md +++ b/x-pack/plugins/encrypted_saved_objects/README.md @@ -9,17 +9,16 @@ security and spaces filtering as well as performing audit logging. ## Usage -Follow these steps to use `encrypted_saved_objects` in your plugin: +Follow these steps to use `encryptedSavedObjects` in your plugin: -1. Declare `encrypted_saved_objects` as a dependency: +1. Declare `encryptedSavedObjects` as a dependency in `kibana.json`: -```typescript -... -new kibana.Plugin({ +```json +{ ... - require: ['encrypted_saved_objects'], + "requiredPlugins": ["encryptedSavedObjects"], ... -}); +} ``` 2. Add attributes to be encrypted in `mappings.json` file for the respective Saved Object type. These attributes should @@ -37,13 +36,17 @@ searchable or analyzed: } ``` -3. Register Saved Object type using the provided API: +3. Register Saved Object type using the provided API at the `setup` stage: ```typescript -server.plugins.encrypted_saved_objects.registerType({ - type: 'my-saved-object-type', - attributesToEncrypt: new Set(['mySecret']), -}); +... +public setup(core: CoreSetup, { encryptedSavedObjects }: PluginSetupDependencies) { + encryptedSavedObjects.registerType({ + type: 'my-saved-object-type', + attributesToEncrypt: new Set(['mySecret']), + }); +} +... ``` 4. For any Saved Object operation that does not require retrieval of decrypted content, use standard REST or @@ -51,11 +54,17 @@ programmatic Saved Object API, e.g.: ```typescript ... -async handler(request: Request) { - return await server.savedObjects - .getScopedSavedObjectsClient(request) - .create('my-saved-object-type', { name: 'some name', mySecret: 'non encrypted secret' }); -} +router.get( + { path: '/some-path', validate: false }, + async (context, req, res) => { + return res.ok({ + body: await context.core.savedObjects.client.create( + 'my-saved-object-type', + { name: 'some name', mySecret: 'non encrypted secret' } + ), + }); + } +); ... ``` @@ -63,12 +72,12 @@ async handler(request: Request) { **Note:** As name suggests the method will retrieve the encrypted values and decrypt them on behalf of the internal Kibana user to make it possible to use this method even when user request context is not available (e.g. in background tasks). -Hence this method should only be used wherever consumers would otherwise feel comfortable using `callWithInternalUser` +Hence this method should only be used wherever consumers would otherwise feel comfortable using `callAsInternalUser` and preferably only as a part of the Kibana server routines that are outside of the lifecycle of a HTTP request that a user has control over. ```typescript -const savedObjectWithDecryptedContent = await server.plugins.encrypted_saved_objects.getDecryptedAsInternalUser( +const savedObjectWithDecryptedContent = await encryptedSavedObjects.getDecryptedAsInternalUser( 'my-saved-object-type', 'saved-object-id' ); diff --git a/x-pack/plugins/encrypted_saved_objects/kibana.json b/x-pack/plugins/encrypted_saved_objects/kibana.json new file mode 100644 index 00000000000000..8bf214a4d7c3fa --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "encryptedSavedObjects", + "version": "8.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "encryptedSavedObjects"], + "server": true, + "ui": false +} diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.test.ts b/x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.test.ts similarity index 95% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.test.ts rename to x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.test.ts index 8c286da8308f2a..3553a3bc347796 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.test.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EncryptedSavedObjectsAuditLogger } from './encrypted_saved_objects_audit_logger'; +import { EncryptedSavedObjectsAuditLogger } from './audit_logger'; test('properly logs audit events', () => { const mockInternalAuditLogger = { log: jest.fn() }; - const audit = new EncryptedSavedObjectsAuditLogger(mockInternalAuditLogger); + const audit = new EncryptedSavedObjectsAuditLogger(() => mockInternalAuditLogger); audit.encryptAttributesSuccess(['one', 'two'], { type: 'known-type', diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.ts b/x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.ts similarity index 83% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.ts rename to x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.ts index e6a832a074e03e..9eb0a1e0872c27 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_audit_logger.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/audit/audit_logger.ts @@ -4,16 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectDescriptor, descriptorToArray } from './encrypted_saved_objects_service'; +import { SavedObjectDescriptor, descriptorToArray } from '../crypto'; +import { LegacyAPI } from '../plugin'; /** * Represents all audit events the plugin can log. */ export class EncryptedSavedObjectsAuditLogger { - constructor(private readonly auditLogger: any) {} + constructor(private readonly getAuditLogger: () => LegacyAPI['auditLogger']) {} public encryptAttributeFailure(attributeName: string, descriptor: SavedObjectDescriptor) { - this.auditLogger.log( + this.getAuditLogger().log( 'encrypt_failure', `Failed to encrypt attribute "${attributeName}" for saved object "[${descriptorToArray( descriptor @@ -23,7 +24,7 @@ export class EncryptedSavedObjectsAuditLogger { } public decryptAttributeFailure(attributeName: string, descriptor: SavedObjectDescriptor) { - this.auditLogger.log( + this.getAuditLogger().log( 'decrypt_failure', `Failed to decrypt attribute "${attributeName}" for saved object "[${descriptorToArray( descriptor @@ -36,7 +37,7 @@ export class EncryptedSavedObjectsAuditLogger { attributesNames: readonly string[], descriptor: SavedObjectDescriptor ) { - this.auditLogger.log( + this.getAuditLogger().log( 'encrypt_success', `Successfully encrypted attributes "[${attributesNames}]" for saved object "[${descriptorToArray( descriptor @@ -49,7 +50,7 @@ export class EncryptedSavedObjectsAuditLogger { attributesNames: readonly string[], descriptor: SavedObjectDescriptor ) { - this.auditLogger.log( + this.getAuditLogger().log( 'decrypt_success', `Successfully decrypted attributes "[${attributesNames}]" for saved object "[${descriptorToArray( descriptor diff --git a/x-pack/plugins/encrypted_saved_objects/server/audit/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/audit/index.mock.ts new file mode 100644 index 00000000000000..41f1c9dac7d18f --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/audit/index.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EncryptedSavedObjectsAuditLogger } from './audit_logger'; + +export const encryptedSavedObjectsAuditLoggerMock = { + create() { + return ({ + encryptAttributesSuccess: jest.fn(), + encryptAttributeFailure: jest.fn(), + decryptAttributesSuccess: jest.fn(), + decryptAttributeFailure: jest.fn(), + } as unknown) as jest.Mocked; + }, +}; diff --git a/x-pack/plugins/encrypted_saved_objects/server/audit/index.ts b/x-pack/plugins/encrypted_saved_objects/server/audit/index.ts new file mode 100644 index 00000000000000..97f684ec841fe1 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/audit/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 { EncryptedSavedObjectsAuditLogger } from './audit_logger'; diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.test.ts b/x-pack/plugins/encrypted_saved_objects/server/config.test.ts new file mode 100644 index 00000000000000..7d6632aa56cb18 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/config.test.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. + */ + +jest.mock('crypto', () => ({ randomBytes: jest.fn() })); + +import { first } from 'rxjs/operators'; +import { loggingServiceMock, coreMock } from 'src/core/server/mocks'; +import { createConfig$, ConfigSchema } from './config'; + +describe('config schema', () => { + it('generates proper defaults', () => { + expect(ConfigSchema.validate({})).toMatchInlineSnapshot(` + Object { + "enabled": true, + "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + `); + + expect(ConfigSchema.validate({}, { dist: false })).toMatchInlineSnapshot(` + Object { + "enabled": true, + "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + `); + + expect(ConfigSchema.validate({}, { dist: true })).toMatchInlineSnapshot(` + Object { + "enabled": true, + } + `); + }); + + it('should throw error if xpack.encryptedSavedObjects.encryptionKey is less than 32 characters', () => { + expect(() => + ConfigSchema.validate({ encryptionKey: 'foo' }) + ).toThrowErrorMatchingInlineSnapshot( + `"[encryptionKey]: value is [foo] but it must have a minimum length of [32]."` + ); + + expect(() => + ConfigSchema.validate({ encryptionKey: 'foo' }, { dist: true }) + ).toThrowErrorMatchingInlineSnapshot( + `"[encryptionKey]: value is [foo] but it must have a minimum length of [32]."` + ); + }); +}); + +describe('createConfig$()', () => { + it('should log a warning and set xpack.encryptedSavedObjects.encryptionKey if not set', async () => { + const mockRandomBytes = jest.requireMock('crypto').randomBytes; + mockRandomBytes.mockReturnValue('ab'.repeat(16)); + + const contextMock = coreMock.createPluginInitializerContext({}); + const config = await createConfig$(contextMock) + .pipe(first()) + .toPromise(); + expect(config).toEqual({ encryptionKey: 'ab'.repeat(16) }); + + expect(loggingServiceMock.collect(contextMock.logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Generating a random key for xpack.encryptedSavedObjects.encryptionKey. To be able to decrypt encrypted saved objects attributes after restart, please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml", + ], + ] + `); + }); +}); diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.ts b/x-pack/plugins/encrypted_saved_objects/server/config.ts new file mode 100644 index 00000000000000..c755b7dd9f205f --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/config.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import crypto from 'crypto'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginInitializerContext } from 'src/core/server'; + +export type ConfigType = ReturnType extends Observable + ? P + : ReturnType; + +export const ConfigSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + encryptionKey: schema.conditional( + schema.contextRef('dist'), + true, + schema.maybe(schema.string({ minLength: 32 })), + schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) }) + ), +}); + +export function createConfig$(context: PluginInitializerContext) { + return context.config.create>().pipe( + map(config => { + const logger = context.logger.get('config'); + + let encryptionKey = config.encryptionKey; + if (encryptionKey === undefined) { + logger.warn( + 'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' + + 'To be able to decrypt encrypted saved objects attributes after restart, ' + + 'please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml' + ); + + encryptionKey = crypto.randomBytes(16).toString('hex'); + } + + return { ...config, encryptionKey }; + }) + ); +} diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts similarity index 97% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.test.ts rename to x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts index 2f38bcf96c5839..e1e1a8224aa7b5 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts @@ -6,19 +6,17 @@ jest.mock('@elastic/node-crypto', () => jest.fn()); -import { EncryptedSavedObjectsAuditLogger } from './encrypted_saved_objects_audit_logger'; +import { EncryptedSavedObjectsAuditLogger } from '../audit'; import { EncryptedSavedObjectsService } from './encrypted_saved_objects_service'; import { EncryptionError } from './encryption_error'; +import { loggingServiceMock } from 'src/core/server/mocks'; +import { encryptedSavedObjectsAuditLoggerMock } from '../audit/index.mock'; + let service: EncryptedSavedObjectsService; let mockAuditLogger: jest.Mocked; beforeEach(() => { - mockAuditLogger = { - encryptAttributesSuccess: jest.fn(), - encryptAttributeFailure: jest.fn(), - decryptAttributesSuccess: jest.fn(), - decryptAttributeFailure: jest.fn(), - } as any; + mockAuditLogger = encryptedSavedObjectsAuditLoggerMock.create(); // Call actual `@elastic/node-crypto` by default, but allow to override implementation in tests. jest @@ -27,8 +25,7 @@ beforeEach(() => { service = new EncryptedSavedObjectsService( 'encryption-key-abc', - ['known-type-1', 'known-type-2'], - { debug: jest.fn(), error: jest.fn() } as any, + loggingServiceMock.create().get(), mockAuditLogger ); }); @@ -54,12 +51,6 @@ describe('#registerType', () => { service.registerType({ type: 'known-type-1', attributesToEncrypt: new Set(['attr']) }) ).toThrowError('The "known-type-1" saved object type is already registered.'); }); - - it('throws if `type` references to the unknown type', () => { - expect(() => - service.registerType({ type: 'unknown-type', attributesToEncrypt: new Set(['attr']) }) - ).toThrowError('The type "unknown-type" is not known saved object type.'); - }); }); describe('#isRegistered', () => { @@ -137,8 +128,7 @@ describe('#encryptAttributes', () => { service = new EncryptedSavedObjectsService( 'encryption-key-abc', - ['known-type-1', 'known-type-2'], - { debug: jest.fn(), error: jest.fn() } as any, + loggingServiceMock.create().get(), mockAuditLogger ); }); @@ -773,8 +763,7 @@ describe('#decryptAttributes', () => { it('fails if encrypted with another encryption key', async () => { service = new EncryptedSavedObjectsService( 'encryption-key-abc*', - ['known-type-1'], - { debug: jest.fn(), error: jest.fn() } as any, + loggingServiceMock.create().get(), mockAuditLogger ); diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts similarity index 93% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.ts rename to x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts index 558f9127c42730..94c16845295774 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts @@ -8,8 +8,8 @@ import nodeCrypto from '@elastic/node-crypto'; import stringify from 'json-stable-stringify'; import typeDetect from 'type-detect'; -import { Server } from 'kibana'; -import { EncryptedSavedObjectsAuditLogger } from './encrypted_saved_objects_audit_logger'; +import { Logger } from 'src/core/server'; +import { EncryptedSavedObjectsAuditLogger } from '../audit'; import { EncryptionError } from './encryption_error'; /** @@ -62,14 +62,12 @@ export class EncryptedSavedObjectsService { /** * @param encryptionKey The key used to encrypt and decrypt saved objects attributes. - * @param knownTypes The list of all known saved object types. - * @param log Ordinary logger instance. + * @param logger Ordinary logger instance. * @param audit Audit logger instance. */ constructor( encryptionKey: string, - private readonly knownTypes: readonly string[], - private readonly log: Server.Logger, + private readonly logger: Logger, private readonly audit: EncryptedSavedObjectsAuditLogger ) { this.crypto = nodeCrypto({ encryptionKey }); @@ -91,10 +89,6 @@ export class EncryptedSavedObjectsService { throw new Error(`The "${typeRegistration.type}" saved object type is already registered.`); } - if (!this.knownTypes.includes(typeRegistration.type)) { - throw new Error(`The type "${typeRegistration.type}" is not known saved object type.`); - } - this.typeRegistrations.set(typeRegistration.type, typeRegistration); } @@ -160,7 +154,9 @@ export class EncryptedSavedObjectsService { encryptionAAD ); } catch (err) { - this.log.error(`Failed to encrypt "${attributeName}" attribute: ${err.message || err}`); + this.logger.error( + `Failed to encrypt "${attributeName}" attribute: ${err.message || err}` + ); this.audit.encryptAttributeFailure(attributeName, descriptor); throw new EncryptionError( @@ -176,7 +172,7 @@ export class EncryptedSavedObjectsService { // not the case we should collect and log them to make troubleshooting easier. const encryptedAttributesKeys = Object.keys(encryptedAttributes); if (encryptedAttributesKeys.length !== typeRegistration.attributesToEncrypt.size) { - this.log.debug( + this.logger.debug( `The following attributes of saved object "${descriptorToArray( descriptor )}" should have been encrypted: ${Array.from( @@ -238,7 +234,7 @@ export class EncryptedSavedObjectsService { encryptionAAD ); } catch (err) { - this.log.error(`Failed to decrypt "${attributeName}" attribute: ${err.message || err}`); + this.logger.error(`Failed to decrypt "${attributeName}" attribute: ${err.message || err}`); this.audit.decryptAttributeFailure(attributeName, descriptor); throw new EncryptionError( @@ -253,7 +249,7 @@ export class EncryptedSavedObjectsService { // not the case we should collect and log them to make troubleshooting easier. const decryptedAttributesKeys = Object.keys(decryptedAttributes); if (decryptedAttributesKeys.length !== typeRegistration.attributesToEncrypt.size) { - this.log.debug( + this.logger.debug( `The following attributes of saved object "${descriptorToArray( descriptor )}" should have been decrypted: ${Array.from( @@ -298,8 +294,8 @@ export class EncryptedSavedObjectsService { } } - if (Object.keys(attributesAAD).length) { - this.log.debug( + if (Object.keys(attributesAAD).length === 0) { + this.logger.debug( `The AAD for saved object "${descriptorToArray( descriptor )}" does not include any attributes.` diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encryption_error.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_error.test.ts similarity index 100% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encryption_error.test.ts rename to x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_error.test.ts diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encryption_error.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_error.ts similarity index 100% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encryption_error.ts rename to x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_error.ts diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts new file mode 100644 index 00000000000000..b1795b9439808e --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts @@ -0,0 +1,62 @@ +/* + * 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 { + EncryptedSavedObjectsService, + EncryptedSavedObjectTypeRegistration, + SavedObjectDescriptor, +} from '.'; + +export const encryptedSavedObjectsServiceMock = { + create(registrations: EncryptedSavedObjectTypeRegistration[] = []) { + const mock: jest.Mocked = new (jest.requireMock( + './encrypted_saved_objects_service' + )).EncryptedSavedObjectsService(); + + function processAttributes>( + descriptor: Pick, + attrs: T, + action: (attrs: T, attrName: string) => void + ) { + const registration = registrations.find(r => r.type === descriptor.type); + if (!registration) { + return attrs; + } + + const clonedAttrs = { ...attrs }; + for (const attrName of registration.attributesToEncrypt) { + if (attrName in clonedAttrs) { + action(clonedAttrs, attrName); + } + } + return clonedAttrs; + } + + mock.isRegistered.mockImplementation( + type => registrations.findIndex(r => r.type === type) >= 0 + ); + mock.encryptAttributes.mockImplementation(async (descriptor, attrs) => + processAttributes( + descriptor, + attrs, + (clonedAttrs, attrName) => (clonedAttrs[attrName] = `*${clonedAttrs[attrName]}*`) + ) + ); + mock.decryptAttributes.mockImplementation(async (descriptor, attrs) => + processAttributes( + descriptor, + attrs, + (clonedAttrs, attrName) => + (clonedAttrs[attrName] = (clonedAttrs[attrName] as string).slice(1, -1)) + ) + ); + mock.stripEncryptedAttributes.mockImplementation((type, attrs) => + processAttributes({ type }, attrs, (clonedAttrs, attrName) => delete clonedAttrs[attrName]) + ); + + return mock; + }, +}; diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/index.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts similarity index 69% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/index.ts rename to x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts index e60342ebca54d6..0849f0eb320dd3 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/index.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts @@ -7,7 +7,7 @@ export { EncryptedSavedObjectsService, EncryptedSavedObjectTypeRegistration, + descriptorToArray, + SavedObjectDescriptor, } from './encrypted_saved_objects_service'; export { EncryptionError } from './encryption_error'; -export { EncryptedSavedObjectsAuditLogger } from './encrypted_saved_objects_audit_logger'; -export { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper'; diff --git a/x-pack/plugins/encrypted_saved_objects/server/index.ts b/x-pack/plugins/encrypted_saved_objects/server/index.ts new file mode 100644 index 00000000000000..5e6edb95ec37a6 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/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. + */ + +import { PluginInitializerContext } from 'src/core/server'; +import { ConfigSchema } from './config'; +import { Plugin } from './plugin'; + +export { EncryptedSavedObjectTypeRegistration, EncryptionError } from './crypto'; +export { PluginSetupContract, PluginStartContract } from './plugin'; + +export const config = { schema: ConfigSchema }; +export const plugin = (initializerContext: PluginInitializerContext) => + new Plugin(initializerContext); diff --git a/x-pack/plugins/encrypted_saved_objects/server/mocks.ts b/x-pack/plugins/encrypted_saved_objects/server/mocks.ts new file mode 100644 index 00000000000000..87c36381a841aa --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/mocks.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 { PluginSetupContract, PluginStartContract } from './plugin'; + +function createEncryptedSavedObjectsSetupMock() { + return { + registerType: jest.fn(), + __legacyCompat: { registerLegacyAPI: jest.fn() }, + } as jest.Mocked; +} + +function createEncryptedSavedObjectsStartMock() { + return { + isEncryptionError: jest.fn(), + getDecryptedAsInternalUser: jest.fn(), + } as jest.Mocked; +} + +export const encryptedSavedObjectsMock = { + createSetup: createEncryptedSavedObjectsSetupMock, + createStart: createEncryptedSavedObjectsStartMock, +}; diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts new file mode 100644 index 00000000000000..534ed13ba0acbd --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.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 { Plugin } from './plugin'; + +import { coreMock } from 'src/core/server/mocks'; + +describe('EncryptedSavedObjects Plugin', () => { + describe('setup()', () => { + it('exposes proper contract', async () => { + const plugin = new Plugin(coreMock.createPluginInitializerContext()); + await expect(plugin.setup(coreMock.createSetup())).resolves.toMatchInlineSnapshot(` + Object { + "__legacyCompat": Object { + "registerLegacyAPI": [Function], + }, + "registerType": [Function], + } + `); + }); + }); + + describe('start()', () => { + it('exposes proper contract', async () => { + const plugin = new Plugin(coreMock.createPluginInitializerContext()); + await plugin.setup(coreMock.createSetup()); + await expect(plugin.start()).toMatchInlineSnapshot(` + Object { + "getDecryptedAsInternalUser": [Function], + "isEncryptionError": [Function], + } + `); + }); + }); +}); diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts new file mode 100644 index 00000000000000..73c2439f777ccc --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -0,0 +1,113 @@ +/* + * 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 { + Logger, + SavedObjectsBaseOptions, + PluginInitializerContext, + CoreSetup, + SavedObjectsLegacyService, + KibanaRequest, + LegacyRequest, +} from 'src/core/server'; +import { first } from 'rxjs/operators'; +import { createConfig$ } from './config'; +import { + EncryptedSavedObjectsService, + EncryptedSavedObjectTypeRegistration, + EncryptionError, +} from './crypto'; +import { EncryptedSavedObjectsAuditLogger } from './audit'; +import { SavedObjectsSetup, setupSavedObjects } from './saved_objects'; + +export interface PluginSetupContract { + registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => void; + __legacyCompat: { registerLegacyAPI: (legacyAPI: LegacyAPI) => void }; +} + +export interface PluginStartContract extends SavedObjectsSetup { + isEncryptionError: (error: Error) => boolean; +} + +/** + * Describes a set of APIs that is available in the legacy platform only and required by this plugin + * to function properly. + */ +export interface LegacyAPI { + savedObjects: SavedObjectsLegacyService; + auditLogger: { + log: (eventType: string, message: string, data?: Record) => void; + }; +} + +/** + * Represents EncryptedSavedObjects Plugin instance that will be managed by the Kibana plugin system. + */ +export class Plugin { + private readonly logger: Logger; + private savedObjectsSetup?: ReturnType; + + private legacyAPI?: LegacyAPI; + private readonly getLegacyAPI = () => { + if (!this.legacyAPI) { + throw new Error('Legacy API is not registered!'); + } + return this.legacyAPI; + }; + + constructor(private readonly initializerContext: PluginInitializerContext) { + this.logger = this.initializerContext.logger.get(); + } + + public async setup(core: CoreSetup): Promise { + const config = await createConfig$(this.initializerContext) + .pipe(first()) + .toPromise(); + const adminClusterClient = await core.elasticsearch.adminClient$.pipe(first()).toPromise(); + + const service = Object.freeze( + new EncryptedSavedObjectsService( + config.encryptionKey, + this.logger, + new EncryptedSavedObjectsAuditLogger(() => this.getLegacyAPI().auditLogger) + ) + ); + + return { + registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => + service.registerType(typeRegistration), + __legacyCompat: { + registerLegacyAPI: (legacyAPI: LegacyAPI) => { + this.legacyAPI = legacyAPI; + this.savedObjectsSetup = setupSavedObjects({ + adminClusterClient, + service, + savedObjects: legacyAPI.savedObjects, + }); + }, + }, + }; + } + + public start() { + this.logger.debug('Starting plugin'); + + return { + isEncryptionError: (error: Error) => error instanceof EncryptionError, + getDecryptedAsInternalUser: (type: string, id: string, options?: SavedObjectsBaseOptions) => { + if (!this.savedObjectsSetup) { + throw new Error('Legacy SavedObjects API is not registered!'); + } + + return this.savedObjectsSetup.getDecryptedAsInternalUser(type, id, options); + }, + }; + } + + public stop() { + this.logger.debug('Stopping plugin'); + } +} diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.test.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts similarity index 93% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.test.ts rename to x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts index a9f41513fbf146..8574293e3e6a66 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts @@ -6,18 +6,19 @@ jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('uuid-v4-id') })); -import { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper'; -import { EncryptedSavedObjectsService } from './encrypted_saved_objects_service'; -import { createEncryptedSavedObjectsServiceMock } from './encrypted_saved_objects_service.mock'; -import { savedObjectsClientMock } from 'src/core/server/saved_objects/service/saved_objects_client.mock'; import { SavedObjectsClientContract } from 'src/core/server'; +import { EncryptedSavedObjectsService } from '../crypto'; +import { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper'; + +import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { encryptedSavedObjectsServiceMock } from '../crypto/index.mock'; let wrapper: EncryptedSavedObjectsClientWrapper; let mockBaseClient: jest.Mocked; -let encryptedSavedObjectsServiceMock: jest.Mocked; +let encryptedSavedObjectsServiceMockInstance: jest.Mocked; beforeEach(() => { mockBaseClient = savedObjectsClientMock.create(); - encryptedSavedObjectsServiceMock = createEncryptedSavedObjectsServiceMock([ + encryptedSavedObjectsServiceMockInstance = encryptedSavedObjectsServiceMock.create([ { type: 'known-type', attributesToEncrypt: new Set(['attrSecret']), @@ -25,7 +26,7 @@ beforeEach(() => { ]); wrapper = new EncryptedSavedObjectsClientWrapper({ - service: encryptedSavedObjectsServiceMock, + service: encryptedSavedObjectsServiceMockInstance, baseClient: mockBaseClient, } as any); }); @@ -76,8 +77,8 @@ describe('#create', () => { attributes: { attrOne: 'one', attrThree: 'three' }, }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'uuid-v4-id' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -107,8 +108,8 @@ describe('#create', () => { attributes: { attrOne: 'one', attrThree: 'three' }, }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'uuid-v4-id', namespace: 'some-namespace' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -236,8 +237,8 @@ describe('#bulkCreate', () => { ], }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'uuid-v4-id' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -272,8 +273,8 @@ describe('#bulkCreate', () => { ], }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'uuid-v4-id', namespace: 'some-namespace' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -390,12 +391,12 @@ describe('#bulkUpdate', () => { ], }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(2); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(2); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'some-id' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'some-id-2' }, { attrOne: 'one 2', attrSecret: 'secret 2', attrThree: 'three 2' } ); @@ -459,8 +460,8 @@ describe('#bulkUpdate', () => { ], }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'some-id', namespace: 'some-namespace' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -822,8 +823,8 @@ describe('#update', () => { attributes: { attrOne: 'one', attrThree: 'three' }, }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'some-id' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); @@ -849,8 +850,8 @@ describe('#update', () => { attributes: { attrOne: 'one', attrThree: 'three' }, }); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledTimes(1); - expect(encryptedSavedObjectsServiceMock.encryptAttributes).toHaveBeenCalledWith( + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjectsServiceMockInstance.encryptAttributes).toHaveBeenCalledWith( { type: 'known-type', id: 'some-id', namespace: 'some-namespace' }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' } ); diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts similarity index 98% rename from x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts rename to x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts index 7fa066aa3355cf..1e439b68f8c30d 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts @@ -21,7 +21,7 @@ import { SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, } from 'src/core/server'; -import { EncryptedSavedObjectsService } from './encrypted_saved_objects_service'; +import { EncryptedSavedObjectsService } from '../crypto'; interface EncryptedSavedObjectsClientOptions { baseClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts new file mode 100644 index 00000000000000..e5445e187e2cc1 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.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 { + IClusterClient, + SavedObject, + SavedObjectAttributes, + SavedObjectsBaseOptions, +} from 'src/core/server'; +import { LegacyAPI } from '../plugin'; +import { EncryptedSavedObjectsService } from '../crypto'; +import { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper'; + +interface SetupSavedObjectsParams { + adminClusterClient: IClusterClient; + service: PublicMethodsOf; + savedObjects: LegacyAPI['savedObjects']; +} + +export interface SavedObjectsSetup { + getDecryptedAsInternalUser: ( + type: string, + id: string, + options?: SavedObjectsBaseOptions + ) => Promise>; +} + +export function setupSavedObjects({ + adminClusterClient, + service, + savedObjects, +}: SetupSavedObjectsParams): SavedObjectsSetup { + // Register custom saved object client that will encrypt, decrypt and strip saved object + // attributes where appropriate for any saved object repository request. We choose max possible + // priority for this wrapper to allow all other wrappers to set proper `namespace` for the Saved + // Object (e.g. wrapper registered by the Spaces plugin) before we encrypt attributes since + // `namespace` is included into AAD. + savedObjects.addScopedSavedObjectsClientWrapperFactory( + Number.MAX_SAFE_INTEGER, + 'encryptedSavedObjects', + ({ client: baseClient }) => new EncryptedSavedObjectsClientWrapper({ baseClient, service }) + ); + + const internalRepository = savedObjects.getSavedObjectsRepository( + adminClusterClient.callAsInternalUser + ); + + return { + getDecryptedAsInternalUser: async ( + type: string, + id: string, + options?: SavedObjectsBaseOptions + ): Promise> => { + const savedObject = await internalRepository.get(type, id, options); + return { + ...savedObject, + attributes: await service.decryptAttributes( + { type, id, namespace: options && options.namespace }, + savedObject.attributes + ), + }; + }, + }; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index eb748a5739b34f..cd917b153f2673 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8348,7 +8348,6 @@ "xpack.searchProfiler.basicLicenseTitle": "ベーシック", "xpack.searchProfiler.formIndexLabel": "インデックス", "xpack.searchProfiler.formProfileButtonLabel": "プロフィール", - "xpack.searchProfiler.formTypeLabel": "タイプ", "xpack.searchProfiler.goldLicenseTitle": "ゴールド", "xpack.searchProfiler.highlightDetails.descriptionTitle": "説明", "xpack.searchProfiler.highlightDetails.selfTimeTitle": "セルフタイム", @@ -8358,7 +8357,7 @@ "xpack.searchProfiler.highlightDetails.totalTimeTooltip": "子を除き、このクエリコンポーネントだけに使用された合計時間です", "xpack.searchProfiler.highlightDetails.typeTitle": "タイプ", "xpack.searchProfiler.licenseErrorMessageDescription": "さらに可視化するには有効なライセンス ({licenseTypeList} または {platinumLicenseType}), が必要ですが、クラスターに見つかりませんでした。", - "xpack.searchProfiler.licenseErrorMessageTitle": "{warningIcon} ライセンスエラー", + "xpack.searchProfiler.licenseErrorMessageTitle": "ライセンスエラー", "xpack.searchProfiler.licenseHasExpiredMessage": "検索プロフィールを利用できません。ライセンスが期限切れです。", "xpack.searchProfiler.pageDisplayName": "検索プロファイラー", "xpack.searchProfiler.platinumLicenseTitle": "プラチナ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 053fe90b813792..80a1cbabdd3a28 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8503,7 +8503,6 @@ "xpack.searchProfiler.basicLicenseTitle": "基础级", "xpack.searchProfiler.formIndexLabel": "索引", "xpack.searchProfiler.formProfileButtonLabel": "配置文件", - "xpack.searchProfiler.formTypeLabel": "类型", "xpack.searchProfiler.goldLicenseTitle": "黄金级", "xpack.searchProfiler.highlightDetails.descriptionTitle": "描述", "xpack.searchProfiler.highlightDetails.selfTimeTitle": "独自时间", @@ -8513,7 +8512,7 @@ "xpack.searchProfiler.highlightDetails.totalTimeTooltip": "此查询组件花费的总时间(包括子项)", "xpack.searchProfiler.highlightDetails.typeTitle": "类型", "xpack.searchProfiler.licenseErrorMessageDescription": "分析器可视化需要有效的许可({licenseTypeList}或{platinumLicenseType},但在您的集群中未找到任何许可。", - "xpack.searchProfiler.licenseErrorMessageTitle": "{warningIcon} 许可错误", + "xpack.searchProfiler.licenseErrorMessageTitle": "许可错误", "xpack.searchProfiler.licenseHasExpiredMessage": "Search Profiler 不可用 - 许可已过期。", "xpack.searchProfiler.pageDisplayName": "Search Profiler", "xpack.searchProfiler.platinumLicenseTitle": "白金级", diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 2ac8fff6ef8ab7..7b2c2c2b7840f1 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -13,7 +13,11 @@ require('@kbn/test').runTestsCli([ require.resolve('../test/api_integration/config_security_basic.js'), require.resolve('../test/api_integration/config.js'), require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), - require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), + // FLAKY: https://github.com/elastic/kibana/issues/50079 + // FLAKY: https://github.com/elastic/kibana/issues/50074 + // FLAKY: https://github.com/elastic/kibana/issues/48709 + // FLAKY: https://github.com/elastic/kibana/issues/50078 + // require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), require.resolve('../test/plugin_api_integration/config.js'), require.resolve('../test/kerberos_api_integration/config'), require.resolve('../test/kerberos_api_integration/anonymous_access.config'), diff --git a/x-pack/scripts/functional_tests_server.js b/x-pack/scripts/functional_tests_server.js index c361f1839c50b7..039da38c8f5479 100644 --- a/x-pack/scripts/functional_tests_server.js +++ b/x-pack/scripts/functional_tests_server.js @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +process.env.ALLOW_PERFORMANCE_HOOKS_IN_TASK_MANAGER = true; + require('@kbn/plugin-helpers').babelRegister(); require('@kbn/test').startServersCli( require.resolve('../test/functional/config.js'), diff --git a/x-pack/test/accessibility/apps/login_page.ts b/x-pack/test/accessibility/apps/login_page.ts new file mode 100644 index 00000000000000..bce6a677453777 --- /dev/null +++ b/x-pack/test/accessibility/apps/login_page.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const a11y = getService('a11y'); + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common', 'security']); + + describe('Security', () => { + describe('Login Page', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.security.logout(); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + afterEach(async () => { + await PageObjects.security.logout(); + }); + + it('meets a11y requirements', async () => { + await PageObjects.common.navigateToApp('login'); + + await retry.waitFor( + 'login page visible', + async () => await testSubjects.exists('loginSubmit') + ); + + await a11y.testAppSnapshot(); + }); + }); + }); +} diff --git a/x-pack/test/accessibility/config.ts b/x-pack/test/accessibility/config.ts new file mode 100644 index 00000000000000..e1a90efccaa807 --- /dev/null +++ b/x-pack/test/accessibility/config.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 { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +export default async function({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('./apps/login_page')], + + pageObjects, + services, + + junit: { + reportName: 'X-Pack Accessibility Tests', + }, + }; +} diff --git a/x-pack/test/accessibility/ftr_provider_context.d.ts b/x-pack/test/accessibility/ftr_provider_context.d.ts new file mode 100644 index 00000000000000..bb257cdcbfe1b5 --- /dev/null +++ b/x-pack/test/accessibility/ftr_provider_context.d.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; + +import { pageObjects } from './page_objects'; +import { services } from './services'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/accessibility/page_objects.ts b/x-pack/test/accessibility/page_objects.ts new file mode 100644 index 00000000000000..abfa2a700db5f6 --- /dev/null +++ b/x-pack/test/accessibility/page_objects.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 { pageObjects } from '../functional/page_objects'; diff --git a/x-pack/test/accessibility/services.ts b/x-pack/test/accessibility/services.ts new file mode 100644 index 00000000000000..a74b97de36b986 --- /dev/null +++ b/x-pack/test/accessibility/services.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { services as kibanaA11yServices } from '../../../test/accessibility/services'; +import { services as functionalServices } from '../functional/services'; + +export const services = { + ...kibanaA11yServices, + ...functionalServices, +}; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts index 9797e20357176d..b2e69a21218256 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts @@ -15,6 +15,13 @@ export default function(kibana: any) { name: 'alerts', init(server: any) { // Action types + const noopActionType: ActionType = { + id: 'test.noop', + name: 'Test: Noop', + async executor() { + return { status: 'ok' }; + }, + }; const indexRecordActionType: ActionType = { id: 'test.index-record', name: 'Test: Index Record', @@ -158,6 +165,7 @@ export default function(kibana: any) { }; }, }; + server.plugins.actions.setup.registerType(noopActionType); server.plugins.actions.setup.registerType(indexRecordActionType); server.plugins.actions.setup.registerType(failingActionType); server.plugins.actions.setup.registerType(rateLimitedActionType); @@ -311,7 +319,7 @@ export default function(kibana: any) { const noopAlertType: AlertType = { id: 'test.noop', name: 'Test: Noop', - actionGroups: [], + actionGroups: ['default'], async executor({ services, params, state }: AlertExecutorOptions) {}, }; server.plugins.alerting.setup.registerType(alwaysFiringAlertType); diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index 83863ef11c4c93..2d1685063f0bc3 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -178,6 +178,7 @@ export class AlertUtils { } const response = await request.send({ enabled: true, + name: 'abc', interval: '1m', throttle: '1m', alertTypeId: 'test.always-firing', diff --git a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts index de08a34b355488..ed6775a0fb9845 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts @@ -7,6 +7,7 @@ export function getTestAlertData(overwrites = {}) { return { enabled: true, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', throttle: '1m', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts index d0780005635b34..1c6d5480e400f2 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -73,6 +73,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { config: { unencrypted: `This value shouldn't get encrypted`, }, + referencedByCount: 0, }, ], }); @@ -133,6 +134,85 @@ export default function findActionTests({ getService }: FtrProviderContext) { config: { unencrypted: `This value shouldn't get encrypted`, }, + referencedByCount: 0, + }, + ], + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle find request appropriately with proper referencedByCount', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/action`) + .set('kbn-xsrf', 'foo') + .send({ + description: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action'); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + actions: [ + { + group: 'default', + id: createdAction.id, + params: {}, + }, + ], + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'alert'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix( + space.id + )}/api/action/_find?filter=action.attributes.actionTypeId:test.index-record` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + page: 1, + perPage: 20, + total: 1, + data: [ + { + id: createdAction.id, + description: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + referencedByCount: 1, }, ], }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index 4d4bdfa1558b63..21b27cfb6f8f85 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -54,6 +54,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, response.body.id, 'alert'); expect(response.body).to.eql({ id: response.body.id, + name: 'abc', actions: [], enabled: true, alertTypeId: 'test.noop', @@ -171,10 +172,10 @@ export default function createAlertTests({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'child "alertTypeId" fails because ["alertTypeId" is required]. child "interval" fails because ["interval" is required]. child "alertTypeParams" fails because ["alertTypeParams" is required]. child "actions" fails because ["actions" is required]', + 'child "name" fails because ["name" is required]. child "alertTypeId" fails because ["alertTypeId" is required]. child "interval" fails because ["interval" is required]. child "alertTypeParams" fails because ["alertTypeParams" is required]. child "actions" fails because ["actions" is required]', validation: { source: 'payload', - keys: ['alertTypeId', 'interval', 'alertTypeParams', 'actions'], + keys: ['name', 'alertTypeId', 'interval', 'alertTypeParams', 'actions'], }, }); break; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index d8cf5327f63793..935ae3ed969153 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -56,6 +56,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', enabled: true, @@ -111,6 +112,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', enabled: true, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index a40fc0a9ac86e1..db798b0d8bc7c3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -50,6 +50,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ id: createdAlert.id, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', enabled: true, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index 5736b82dffec5f..ae5853eead7e7a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -31,6 +31,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAlert.id, 'alert'); const updatedData = { + name: 'bcd', alertTypeParams: { foo: true, }, @@ -89,6 +90,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ + name: 'bcd', alertTypeParams: { foo: true, }, @@ -134,6 +136,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ + name: 'bcd', throttle: '1m', alertTypeId: '1', alertTypeParams: { @@ -197,10 +200,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'child "throttle" fails because ["throttle" is required]. child "interval" fails because ["interval" is required]. child "alertTypeParams" fails because ["alertTypeParams" is required]. child "actions" fails because ["actions" is required]', + 'child "throttle" fails because ["throttle" is required]. child "name" fails because ["name" is required]. child "interval" fails because ["interval" is required]. child "alertTypeParams" fails because ["alertTypeParams" is required]. child "actions" fails because ["actions" is required]', validation: { source: 'payload', - keys: ['throttle', 'interval', 'alertTypeParams', 'actions'], + keys: ['throttle', 'name', 'interval', 'alertTypeParams', 'actions'], }, }); break; @@ -229,6 +232,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ + name: 'bcd', interval: '10s', throttle: '1m', alertTypeParams: {}, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index a643bd8f9f64fc..af9804473a4489 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -18,7 +18,11 @@ export default function alertingApiIntegrationTests({ const spacesService: SpacesService = getService('spaces'); const esArchiver = getService('esArchiver'); - describe('alerting api integration security and spaces enabled', function() { + // FLAKY: https://github.com/elastic/kibana/issues/50079 + // FLAKY: https://github.com/elastic/kibana/issues/50074 + // FLAKY: https://github.com/elastic/kibana/issues/48709 + // FLAKY: https://github.com/elastic/kibana/issues/50078 + describe.skip('alerting api integration security and spaces enabled', function() { this.tags('ciGroup1'); before(async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts index 3e61242344c2f5..20ea3f30443108 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts @@ -52,6 +52,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { config: { unencrypted: `This value shouldn't get encrypted`, }, + referencedByCount: 0, }, ], }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index 10335633f5d28e..6ad6a54d3dccbf 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -36,6 +36,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { objectRemover.add(Spaces.space1.id, response.body.id, 'alert'); expect(response.body).to.eql({ id: response.body.id, + name: 'abc', actions: [], enabled: true, alertTypeId: 'test.noop', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 521befeb490471..97892e3a95d3ff 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -39,6 +39,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', enabled: true, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index cd30b65cb180ee..391d78dda6647f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -33,6 +33,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ id: createdAlert.id, + name: 'abc', alertTypeId: 'test.noop', interval: '10s', enabled: true, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts index d98f6b5cd830b3..b5d9d477f7c82c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts @@ -26,6 +26,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert'); const updatedData = { + name: 'bcd', alertTypeParams: { foo: true, }, @@ -63,6 +64,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .put(`${getUrlPrefix(Spaces.other.id)}/api/alert/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .send({ + name: 'bcd', alertTypeParams: { foo: true, }, diff --git a/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js b/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js index 49a99b3bcdd939..27435d49c252fa 100644 --- a/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js +++ b/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js @@ -11,7 +11,7 @@ export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const es = getService('es'); - const chance = getService('chance'); + const randomness = getService('randomness'); describe('assign_tags_to_beats', () => { const archive = 'beats/list'; @@ -179,9 +179,8 @@ export default function ({ getService }) { expect(beat.tags).to.eql(['production']); }); - // FLAKY: https://github.com/elastic/kibana/issues/34038 - it.skip('should return errors for non-existent beats', async () => { - const nonExistentBeatId = chance.word(); + it('should return errors for non-existent beats', async () => { + const nonExistentBeatId = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/assignments') @@ -197,7 +196,7 @@ export default function ({ getService }) { }); it('should return errors for non-existent tags', async () => { - const nonExistentTag = chance.word(); + const nonExistentTag = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/assignments') @@ -221,8 +220,8 @@ export default function ({ getService }) { }); it('should return errors for non-existent beats and tags', async () => { - const nonExistentBeatId = chance.word(); - const nonExistentTag = chance.word(); + const nonExistentBeatId = randomness.word(); + const nonExistentTag = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/assignments') diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js index b94af7215edf7e..4b4767e1d98497 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -11,7 +11,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - const chance = getService('chance'); + const randomness = getService('randomness'); const es = getService('es'); describe('enroll_beat', () => { @@ -20,20 +20,20 @@ export default function ({ getService }) { let beat; beforeEach(async () => { - validEnrollmentToken = chance.word(); + validEnrollmentToken = randomness.word(); - beatId = chance.word(); + beatId = randomness.word(); const version = - chance.integer({ min: 1, max: 10 }) + + randomness.integer({ min: 1, max: 10 }) + '.' + - chance.integer({ min: 1, max: 10 }) + + randomness.integer({ min: 1, max: 10 }) + '.' + - chance.integer({ min: 1, max: 10 }); + randomness.integer({ min: 1, max: 10 }); beat = { type: 'filebeat', host_name: 'foo.bar.com', - name: chance.word(), + name: randomness.word(), version, }; @@ -94,7 +94,7 @@ export default function ({ getService }) { const { body: apiResponse } = await supertest .post(`/api/beats/agent/${beatId}`) .set('kbn-xsrf', 'xxx') - .set('kbn-beats-enrollment-token', chance.word()) + .set('kbn-beats-enrollment-token', randomness.word()) .send(beat) .expect(400); diff --git a/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js b/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js index 3e7c6ce8f48ffa..1548aff1182b33 100644 --- a/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js +++ b/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js @@ -11,7 +11,7 @@ export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const es = getService('es'); - const chance = getService('chance'); + const randomness = getService('randomness'); describe('remove_tags_from_beats', () => { const archive = 'beats/list'; @@ -135,7 +135,7 @@ export default function ({ getService }) { }); it('should return errors for non-existent beats', async () => { - const nonExistentBeatId = chance.word(); + const nonExistentBeatId = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/removals') @@ -151,7 +151,7 @@ export default function ({ getService }) { }); it('should return errors for non-existent tags', async () => { - const nonExistentTag = chance.word(); + const nonExistentTag = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/removals') @@ -175,8 +175,8 @@ export default function ({ getService }) { }); it('should return errors for non-existent beats and tags', async () => { - const nonExistentBeatId = chance.word(); - const nonExistentTag = chance.word(); + const nonExistentBeatId = randomness.word(); + const nonExistentTag = randomness.word(); const { body: apiResponse } = await supertest .post('/api/beats/agents_tags/removals') diff --git a/x-pack/test/api_integration/apis/beats/set_config.js b/x-pack/test/api_integration/apis/beats/set_config.js index a4d7d491d4778e..5e15145cf47c8e 100644 --- a/x-pack/test/api_integration/apis/beats/set_config.js +++ b/x-pack/test/api_integration/apis/beats/set_config.js @@ -9,7 +9,6 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - // const chance = getService('chance'); const es = getService('es'); const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/api_integration/apis/beats/update_beat.js b/x-pack/test/api_integration/apis/beats/update_beat.js index 090668aff925c8..228d651a590e6e 100644 --- a/x-pack/test/api_integration/apis/beats/update_beat.js +++ b/x-pack/test/api_integration/apis/beats/update_beat.js @@ -10,7 +10,7 @@ import moment from 'moment'; export default function ({ getService }) { const supertest = getService('supertest'); - const chance = getService('chance'); + const randomness = getService('randomness'); const es = getService('es'); const esArchiver = getService('esArchiver'); @@ -27,18 +27,18 @@ export default function ({ getService }) { 'SSsX2Byyo1B1bGxV8C3G4QldhE5iH87EY_1r21-bwbI'; const version = - chance.integer({ min: 1, max: 10 }) + + randomness.integer({ min: 1, max: 10 }) + '.' + - chance.integer({ min: 1, max: 10 }) + + randomness.integer({ min: 1, max: 10 }) + '.' + - chance.integer({ min: 1, max: 10 }); + randomness.integer({ min: 1, max: 10 }); beat = { - type: `${chance.word()}beat`, - host_name: `www.${chance.word()}.net`, - name: chance.word(), + type: `${randomness.word()}beat`, + host_name: `www.${randomness.word()}.net`, + name: randomness.word(), version, - ephemeral_id: chance.word(), + ephemeral_id: randomness.word(), }; await es.index({ @@ -90,7 +90,7 @@ export default function ({ getService }) { const { body } = await supertest .put(`/api/beats/agent/${beatId}`) .set('kbn-xsrf', 'xxx') - .set('kbn-beats-access-token', chance.word()) + .set('kbn-beats-access-token', randomness.word()) .send(beat) .expect(401); @@ -109,7 +109,7 @@ export default function ({ getService }) { }); it('should return an error for a non-existent beat', async () => { - const beatId = chance.word(); + const beatId = randomness.word(); const { body } = await supertest .put(`/api/beats/agent/${beatId}`) .set('kbn-xsrf', 'xxx') diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_apm.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_apm.json index a791c2b2b7419d..ddd8d8c9a1de66 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_apm.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_apm.json @@ -51,7 +51,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats.json index 3ce2f20415b5f2..fcde71551a4f37 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats.json @@ -60,7 +60,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats_management.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats_management.json index a64e2f40b33dc9..9186006415759e 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats_management.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_beats_management.json @@ -51,7 +51,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash.json index cc870216d405bd..7311230d108e98 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash.json @@ -51,7 +51,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash_management.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash_management.json index cc870216d405bd..7311230d108e98 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash_management.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/detect_logstash_management.json @@ -51,7 +51,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_exclusive_mb.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_exclusive_mb.json index 4ae753aca52251..69688bc46c27eb 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_exclusive_mb.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_exclusive_mb.json @@ -71,7 +71,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_mb.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_mb.json index 6935060b4d5f7e..8b207b418dae74 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_mb.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/es_and_kibana_mb.json @@ -71,7 +71,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_exclusive_mb.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_exclusive_mb.json index 161ce32e8ff5f3..319844a7e093d9 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_exclusive_mb.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_exclusive_mb.json @@ -71,7 +71,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_mb.json b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_mb.json index b93edacd82b310..f65436ce28616d 100644 --- a/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_mb.json +++ b/x-pack/test/api_integration/apis/monitoring/setup/collection/fixtures/kibana_mb.json @@ -71,7 +71,6 @@ }, "_meta": { "secondsAgo": 30, - "isOnCloud": false, "liveClusterUuid": null } } diff --git a/x-pack/test/api_integration/apis/telemetry/index.js b/x-pack/test/api_integration/apis/telemetry/index.js index d941cda9e3faea..6f794d56ae713a 100644 --- a/x-pack/test/api_integration/apis/telemetry/index.js +++ b/x-pack/test/api_integration/apis/telemetry/index.js @@ -8,5 +8,6 @@ export default function ({ loadTestFile }) { describe('Telemetry', () => { loadTestFile(require.resolve('./telemetry')); loadTestFile(require.resolve('./telemetry_local')); + loadTestFile(require.resolve('./opt_in')); }); } diff --git a/x-pack/test/api_integration/apis/telemetry/opt_in.ts b/x-pack/test/api_integration/apis/telemetry/opt_in.ts new file mode 100644 index 00000000000000..d2ad2d773d6924 --- /dev/null +++ b/x-pack/test/api_integration/apis/telemetry/opt_in.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. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function optInTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + describe('/api/telemetry/v2/optIn API', () => { + let kibanaVersion: any; + + before(async () => { + const kibanaVersionAccessor = kibanaServer.version; + kibanaVersion = await kibanaVersionAccessor.get(); + + expect(typeof kibanaVersion).to.eql('string'); + expect(kibanaVersion.length).to.be.greaterThan(0); + }); + + it('should support sending false', async () => { + await postTelemetryV2Optin(supertest, false, 200); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(supertest); + expect(enabled).to.be(false); + expect(lastVersionChecked).to.be(kibanaVersion); + }); + + it('should support sending true', async () => { + await postTelemetryV2Optin(supertest, true, 200); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(supertest); + expect(enabled).to.be(true); + expect(lastVersionChecked).to.be(kibanaVersion); + }); + + it('should not support sending null', async () => { + await postTelemetryV2Optin(supertest, null, 400); + }); + + it('should not support sending junk', async () => { + await postTelemetryV2Optin(supertest, 42, 400); + }); + }); +} + +async function postTelemetryV2Optin(supertest: any, value: any, statusCode: number): Promise { + const { body } = await supertest + .post('/api/telemetry/v2/optIn') + .set('kbn-xsrf', 'xxx') + .send({ enabled: value }) + .expect(statusCode); + + return body; +} + +async function getSavedObjectAttributes(supertest: any): Promise { + const { body } = await supertest.get('/api/saved_objects/telemetry/telemetry').expect(200); + return body.attributes; +} diff --git a/x-pack/test/api_integration/services/index.ts b/x-pack/test/api_integration/services/index.ts index 00ba2a6b3acfee..7e3b747c81993a 100644 --- a/x-pack/test/api_integration/services/index.ts +++ b/x-pack/test/api_integration/services/index.ts @@ -25,14 +25,11 @@ import { SiemGraphQLClientProvider, SiemGraphQLClientFactoryProvider } from './s import { InfraOpsSourceConfigurationProvider } from './infraops_source_configuration'; export const services = { - chance: kibanaApiIntegrationServices.chance, + ...kibanaCommonServices, + esSupertest: kibanaApiIntegrationServices.esSupertest, supertest: kibanaApiIntegrationServices.supertest, - esArchiver: kibanaCommonServices.esArchiver, - kibanaServer: kibanaCommonServices.kibanaServer, - retry: kibanaCommonServices.retry, - es: LegacyEsProvider, esSupertestWithoutAuth: EsSupertestWithoutAuthProvider, infraOpsGraphQLClient: InfraOpsGraphQLClientProvider, diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index 96d4aa10149ea4..cdb904537a0f25 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects }: FtrProviderContext) => { @@ -23,11 +24,45 @@ export default ({ getPageObjects }: FtrProviderContext) => { it('runs filter query without issues', async () => { await pageObjects.uptime.inputFilterQuery( - DEFAULT_DATE_START, - DEFAULT_DATE_END, - 'monitor.status:up and monitor.id:"0000-intermittent"', - 'monitor-page-link-0000-intermittent' + 'monitor.status:up and monitor.id:"0000-intermittent"' ); + await pageObjects.uptime.pageHasExpectedIds(['0000-intermittent']); + }); + + it('pagination is cleared when filter criteria changes', async () => { + await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await pageObjects.uptime.changePage('next'); + // there should now be pagination data in the URL + const contains = await pageObjects.uptime.pageUrlContains('pagination'); + expect(contains).to.be(true); + await pageObjects.uptime.pageHasExpectedIds([ + '0010-down', + '0011-up', + '0012-up', + '0013-up', + '0014-up', + '0015-intermittent', + '0016-up', + '0017-up', + '0018-up', + '0019-up', + ]); + await pageObjects.uptime.setStatusFilter('up'); + // ensure that pagination is removed from the URL + const doesNotContain = await pageObjects.uptime.pageUrlContains('pagination'); + expect(doesNotContain).to.be(false); + await pageObjects.uptime.pageHasExpectedIds([ + '0000-intermittent', + '0001-up', + '0002-up', + '0003-up', + '0004-up', + '0005-up', + '0006-up', + '0007-up', + '0008-up', + '0009-up', + ]); }); }); }; diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index f8cedc03865bb3..d1f03f2c136ff6 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -82,7 +82,7 @@ export default async function ({ readConfigFile }) { '--xpack.reporting.csv.maxSizeBytes=2850', // small-ish limit for cutting off a 1999 byte report '--stats.maximumWaitTimeForAllCollectorsInS=1', '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions - '--xpack.encrypted_saved_objects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', + '--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', '--telemetry.banner=false', '--timelion.ui.enabled=true', ], diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index 229f8ee55d4420..26c95c3bf526d8 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -11,6 +11,14 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo const uptimeService = getService('uptime'); return new (class UptimePage { + public async goToUptimePageAndSetDateRange( + datePickerStartValue: string, + datePickerEndValue: string + ) { + await pageObjects.common.navigateToApp('uptime'); + await pageObjects.timePicker.setAbsoluteRange(datePickerStartValue, datePickerEndValue); + } + public async goToUptimeOverviewAndLoadData( datePickerStartValue: string, datePickerEndValue: string, @@ -35,16 +43,32 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo } } - public async inputFilterQuery( - datePickerStartValue: string, - datePickerEndValue: string, - filterQuery: string, - testId: string - ) { - await pageObjects.common.navigateToApp('uptime'); - await pageObjects.timePicker.setAbsoluteRange(datePickerStartValue, datePickerEndValue); + public async inputFilterQuery(filterQuery: string) { await uptimeService.setFilterText(filterQuery); - await uptimeService.monitorIdExists(testId); + } + + public async pageHasExpectedIds(monitorIdsToCheck: string[]) { + await Promise.all(monitorIdsToCheck.map(id => uptimeService.monitorPageLinkExists(id))); + } + + public async pageUrlContains(value: string) { + return await uptimeService.urlContains(value); + } + + public async changePage(direction: 'next' | 'prev') { + if (direction === 'next') { + await uptimeService.goToNextPage(); + } else if (direction === 'prev') { + await uptimeService.goToPreviousPage(); + } + } + + public async setStatusFilter(value: 'up' | 'down') { + if (value === 'up') { + await uptimeService.setStatusFilterUp(); + } else if (value === 'down') { + await uptimeService.setStatusFilterDown(); + } } })(); } diff --git a/x-pack/test/functional/services/random.js b/x-pack/test/functional/services/random.js index 3ed3a945195f71..1245a278eab619 100644 --- a/x-pack/test/functional/services/random.js +++ b/x-pack/test/functional/services/random.js @@ -13,7 +13,7 @@ export function RandomProvider({ getService }) { log.debug('randomness seed: %j', seed); const chance = new Chance(seed); - return new class Random { + return new class Randomness { int(min = 3, max = 15) { return chance.integer({ min, max }); } diff --git a/x-pack/test/functional/services/uptime.ts b/x-pack/test/functional/services/uptime.ts index fd1a61da2c0f59..40d2e3dafc7f84 100644 --- a/x-pack/test/functional/services/uptime.ts +++ b/x-pack/test/functional/services/uptime.ts @@ -19,6 +19,13 @@ export function UptimeProvider({ getService }: FtrProviderContext) { async monitorIdExists(key: string) { await testSubjects.existOrFail(key); }, + async monitorPageLinkExists(monitorId: string) { + await testSubjects.existOrFail(`monitor-page-link-${monitorId}`); + }, + async urlContains(expected: string) { + const url = await browser.getCurrentUrl(); + return url.indexOf(expected) >= 0; + }, async navigateToMonitorWithId(monitorId: string) { await testSubjects.click(`monitor-page-link-${monitorId}`); }, @@ -30,5 +37,17 @@ export function UptimeProvider({ getService }: FtrProviderContext) { await testSubjects.setValue('xpack.uptime.filterBar', filterQuery); await browser.pressKeys(browser.keys.ENTER); }, + async goToNextPage() { + await testSubjects.click('xpack.uptime.monitorList.nextButton'); + }, + async goToPreviousPage() { + await testSubjects.click('xpack.uptime.monitorList.prevButton'); + }, + async setStatusFilterUp() { + await testSubjects.click('xpack.uptime.filterBar.filterStatusUp'); + }, + async setStatusFilterDown() { + await testSubjects.click('xpack.uptime.filterBar.filterStatusDown'); + }, }; } diff --git a/x-pack/test/plugin_api_integration/plugins/encrypted_saved_objects/index.ts b/x-pack/test/plugin_api_integration/plugins/encrypted_saved_objects/index.ts index 7b39c236ec5437..a194e477da7559 100644 --- a/x-pack/test/plugin_api_integration/plugins/encrypted_saved_objects/index.ts +++ b/x-pack/test/plugin_api_integration/plugins/encrypted_saved_objects/index.ts @@ -7,6 +7,10 @@ import { Request } from 'hapi'; import { boomify, badRequest } from 'boom'; import { Legacy } from 'kibana'; +import { + PluginSetupContract, + PluginStartContract, +} from '../../../../plugins/encrypted_saved_objects/server'; const SAVED_OBJECT_WITH_SECRET_TYPE = 'saved-object-with-secret'; @@ -14,22 +18,24 @@ const SAVED_OBJECT_WITH_SECRET_TYPE = 'saved-object-with-secret'; export default function esoPlugin(kibana: any) { return new kibana.Plugin({ id: 'eso', - require: ['encrypted_saved_objects'], + require: ['encryptedSavedObjects'], uiExports: { mappings: require('./mappings.json') }, init(server: Legacy.Server) { server.route({ method: 'GET', path: '/api/saved_objects/get-decrypted-as-internal-user/{id}', async handler(request: Request) { + const encryptedSavedObjectsStart = server.newPlatform.start.plugins + .encryptedSavedObjects as PluginStartContract; const namespace = server.plugins.spaces && server.plugins.spaces.getSpaceId(request); try { - return await (server.plugins as any).encrypted_saved_objects.getDecryptedAsInternalUser( + return await encryptedSavedObjectsStart.getDecryptedAsInternalUser( SAVED_OBJECT_WITH_SECRET_TYPE, request.params.id, { namespace: namespace === 'default' ? undefined : namespace } ); } catch (err) { - if ((server.plugins as any).encrypted_saved_objects.isEncryptionError(err)) { + if (encryptedSavedObjectsStart.isEncryptionError(err)) { return badRequest('Failed to encrypt attributes'); } @@ -38,7 +44,7 @@ export default function esoPlugin(kibana: any) { }, }); - (server.plugins as any).encrypted_saved_objects.registerType({ + (server.newPlatform.setup.plugins.encryptedSavedObjects as PluginSetupContract).registerType({ type: SAVED_OBJECT_WITH_SECRET_TYPE, attributesToEncrypt: new Set(['privateProperty']), attributesToExcludeFromAAD: new Set(['publicPropertyExcludedFromAAD']), diff --git a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts index 34c8cdcc012bae..7be9816f40de3f 100644 --- a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts +++ b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService }: FtrProviderContext) { const es = getService('es'); - const chance = getService('chance'); + const randomness = getService('randomness'); const supertest = getService('supertest'); const SAVED_OBJECT_WITH_SECRET_TYPE = 'saved-object-with-secret'; @@ -37,9 +37,9 @@ export default function({ getService }: FtrProviderContext) { let savedObject: SavedObject; beforeEach(async () => { savedObjectOriginalAttributes = { - publicProperty: chance.string(), - publicPropertyExcludedFromAAD: chance.string(), - privateProperty: chance.string(), + publicProperty: randomness.string(), + publicPropertyExcludedFromAAD: randomness.string(), + privateProperty: randomness.string(), }; const { body } = await supertest @@ -74,17 +74,17 @@ export default function({ getService }: FtrProviderContext) { { type: SAVED_OBJECT_WITH_SECRET_TYPE, attributes: { - publicProperty: chance.string(), - publicPropertyExcludedFromAAD: chance.string(), - privateProperty: chance.string(), + publicProperty: randomness.string(), + publicPropertyExcludedFromAAD: randomness.string(), + privateProperty: randomness.string(), }, }, { type: SAVED_OBJECT_WITH_SECRET_TYPE, attributes: { - publicProperty: chance.string(), - publicPropertyExcludedFromAAD: chance.string(), - privateProperty: chance.string(), + publicProperty: randomness.string(), + publicPropertyExcludedFromAAD: randomness.string(), + privateProperty: randomness.string(), }, }, ]; @@ -162,9 +162,9 @@ export default function({ getService }: FtrProviderContext) { it('#update encrypts attributes and strips them from response', async () => { const updatedAttributes = { - publicProperty: chance.string(), - publicPropertyExcludedFromAAD: chance.string(), - privateProperty: chance.string(), + publicProperty: randomness.string(), + publicPropertyExcludedFromAAD: randomness.string(), + privateProperty: randomness.string(), }; const { body: response } = await supertest @@ -197,7 +197,7 @@ export default function({ getService }: FtrProviderContext) { }); it('#getDecryptedAsInternalUser is able to decrypt if non-AAD attribute has changed', async () => { - const updatedAttributes = { publicPropertyExcludedFromAAD: chance.string() }; + const updatedAttributes = { publicPropertyExcludedFromAAD: randomness.string() }; const { body: response } = await supertest .put(`${getURLAPIBaseURL()}${SAVED_OBJECT_WITH_SECRET_TYPE}/${savedObject.id}`) @@ -220,7 +220,7 @@ export default function({ getService }: FtrProviderContext) { }); it('#getDecryptedAsInternalUser fails to decrypt if AAD attribute has changed', async () => { - const updatedAttributes = { publicProperty: chance.string() }; + const updatedAttributes = { publicProperty: randomness.string() }; const { body: response } = await supertest .put(`${getURLAPIBaseURL()}${SAVED_OBJECT_WITH_SECRET_TYPE}/${savedObject.id}`) diff --git a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/index.ts b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/index.ts index 834e0225d0fb6c..424160e84495e3 100644 --- a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { - describe('encrypted_saved_objects', function encryptedSavedObjectsSuite() { + describe('encryptedSavedObjects', function encryptedSavedObjectsSuite() { this.tags('ciGroup2'); loadTestFile(require.resolve('./encrypted_saved_objects_api')); }); diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js index 0547069dfb4694..17ae9b26fa3b3b 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js @@ -4,16 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import uuid from 'uuid'; +import _ from 'lodash'; +import stats from 'stats-lite'; +import prettyMilliseconds from 'pretty-ms'; +import { performance, PerformanceObserver } from 'perf_hooks'; import { initRoutes } from './init_routes'; -function avg(items) { - return ( - items.reduce((sum, val) => { - return sum + val; - }, 0) / items.length - ); -} - export default function TaskManagerPerformanceAPI(kibana) { return new kibana.Plugin({ name: 'perfTask', @@ -27,35 +24,46 @@ export default function TaskManagerPerformanceAPI(kibana) { init(server) { const taskManager = server.plugins.task_manager; - const performanceState = { - runningAverageTasks: 0, - averagesTaken: [], - runningAverageLeadTime: -1, - averagesTakenLeadTime: [], - leadTimeQueue: [], - }; + const performanceState = resetPerfState({}); + + let lastFlush = new Date(); + function flushPerfStats() { + setTimeout(flushPerfStats, 5000); + const prevFlush = lastFlush; + lastFlush = new Date(); - setInterval(() => { const tasks = performanceState.leadTimeQueue.length; - console.log(`I have processed ${tasks} tasks in the past 5s`); + const title = `[Perf${performanceState.capturing ? ' (capturing)' : ''}]`; + const seconds = parseInt((lastFlush - prevFlush) / 1000); + console.log( + `${title} I have processed ${tasks} tasks in the past ${seconds}s (${tasks / + seconds} per second)` + ); if (tasks > 0) { - const latestAverage = avg(performanceState.leadTimeQueue.splice(0, tasks)); + const latestAverage = avg(performanceState.leadTimeQueue.splice(0, tasks)).mean; performanceState.averagesTakenLeadTime.push(latestAverage); performanceState.averagesTaken.push(tasks); if (performanceState.averagesTakenLeadTime.length > 1) { - performanceState.runningAverageLeadTime = avg(performanceState.averagesTakenLeadTime); - performanceState.runningAverageTasks = avg(performanceState.averagesTaken); + performanceState.runningAverageLeadTime = avg( + performanceState.averagesTakenLeadTime + ).mean; + performanceState.runningAverageTasksPerSecond = + avg(performanceState.averagesTaken).mean / 5; } else { performanceState.runningAverageLeadTime = latestAverage; - performanceState.runningAverageTasks = tasks; + performanceState.runningAverageTasksPerSecond = tasks / 5; } } - }, 5000); + } + + setTimeout(flushPerfStats, 5000); + + const title = 'Perf Test Task'; taskManager.registerTaskDefinitions({ performanceTestTask: { - title: 'Perf Test Task', + title, description: 'A task for stress testing task_manager.', timeout: '1m', @@ -64,27 +72,41 @@ export default function TaskManagerPerformanceAPI(kibana) { async run() { const { params, state } = taskInstance; - const runAt = millisecondsFromNow(5000); + const counter = state.counter ? state.counter : 1; + const now = Date.now(); const leadTime = now - taskInstance.runAt; performanceState.leadTimeQueue.push(leadTime); - const counter = (state.counter ? 1 + state.counter : 1); + // schedule to run next cycle as soon as possible + const runAt = calRunAt(params, counter); const stateUpdated = { ...state, - counter + counter: counter + 1, }; - if(params.trackExecutionTimeline) { - stateUpdated.timeline = stateUpdated.timeline || []; - stateUpdated.timeline.push({ - owner: taskInstance.owner.split('-')[0], - counter, - leadTime, - ranAt: now - }); + if (params.trackExecutionTimeline && state.perf && state.perf.id) { + performance.mark(`perfTask_run_${state.perf.id}_${counter}`); + performance.measure( + 'perfTask.markUntilRun', + `perfTask_markAsRunning_${state.perf.id}_${counter}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + if (counter === 1) { + performance.measure( + 'perfTask.firstRun', + `perfTask_schedule_${state.perf.id}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + performance.measure( + 'perfTask.firstMarkAsRunningTillRan', + `perfTask_markAsRunning_${state.perf.id}_${counter}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + } } + return { state: stateUpdated, runAt, @@ -95,17 +117,249 @@ export default function TaskManagerPerformanceAPI(kibana) { }, }); - initRoutes(server, performanceState); + taskManager.addMiddleware({ + async beforeSave({ taskInstance, ...opts }) { + const modifiedInstance = { + ...taskInstance, + }; + + if (taskInstance.params && taskInstance.params.trackExecutionTimeline) { + modifiedInstance.state = modifiedInstance.state || {}; + modifiedInstance.state.perf = modifiedInstance.state.perf || {}; + modifiedInstance.state.perf.id = uuid.v4().replace(/-/gi, '_'); + performance.mark(`perfTask_schedule_${modifiedInstance.state.perf.id}`); + } + + return { + ...opts, + taskInstance: modifiedInstance, + }; + }, + + async beforeMarkRunning({ taskInstance, ...opts }) { + const modifiedInstance = { + ...taskInstance, + }; + + if ( + modifiedInstance.state && + modifiedInstance.state.perf && + modifiedInstance.state.perf.id + ) { + const { counter = 1 } = modifiedInstance.state; + performance.mark(`perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}`); + if (counter === 1) { + performance.measure( + 'perfTask.firstMarkAsRunning', + `perfTask_schedule_${modifiedInstance.state.perf.id}`, + `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` + ); + } else if (counter > 1) { + performance.measure( + 'perfTask.runUntilNextMarkAsRunning', + `perfTask_run_${modifiedInstance.state.perf.id}_${counter - 1}`, + `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` + ); + } + } + + return { + ...opts, + taskInstance: modifiedInstance, + }; + }, + }); + + const perfApi = { + capture() { + resetPerfState(performanceState); + performanceState.capturing = true; + performance.mark('perfTest.start'); + }, + endCapture() { + return new Promise(resolve => { + performanceState.performance.summarize.push([resolve, perfApi.summarize]); + + performance.mark('perfTest.end'); + performance.measure('perfTest.duration', 'perfTest.start', 'perfTest.end'); + }); + }, + summarize(perfTestDuration) { + const { + runningAverageTasksPerSecond, + runningAverageLeadTime, + performance, + } = performanceState; + + const { + numberOfTasksRanOverall, + elasticsearchApiCalls, + activityDuration, + sleepDuration, + cycles, + claimAvailableTasksNoTasks, + claimAvailableTasksNoAvailableWorkers, + taskPoolAttemptToRun, + taskRunnerMarkTaskAsRunning + } = performance; + + const perfRes = { + perfTestDuration: prettyMilliseconds(perfTestDuration), + runningAverageTasksPerSecond, + runningAverageLeadTime, + numberOfTasksRanOverall, + claimAvailableTasksNoTasks, + claimAvailableTasksNoAvailableWorkers, + elasticsearchApiCalls: _.mapValues(elasticsearchApiCalls, avg), + sleepDuration: prettyMilliseconds(stats.sum(sleepDuration)), + activityDuration: prettyMilliseconds(stats.sum(activityDuration)), + cycles, + taskPoolAttemptToRun: avg(taskPoolAttemptToRun), + taskRunnerMarkTaskAsRunning: avg(taskRunnerMarkTaskAsRunning), + }; + + resetPerfState(performanceState); + + return perfRes; + }, + }; + + initRoutes(server, perfApi); }, }); } -function millisecondsFromNow(ms) { - if (!ms) { - return; +function calRunAt(params, counter) { + const runAt = counter === 1 ? new Date(params.startAt) : new Date(); + return runAt.getTime() < params.runUntil ? runAt : undefined; +} + +function avg(items) { + const mode = stats.mode(items); + return { + mean: parseInt(stats.mean(items)), + range: { + min: parseInt(typeof mode === 'number' ? mode : _.min([...mode])), + max: parseInt(typeof mode === 'number' ? mode : _.max([...mode])), + }, + }; +} + +function resetPerfState(target) { + if(target.performanceObserver) { + target.performanceObserver.disconnect(); } - const dt = new Date(); - dt.setTime(dt.getTime() + ms); - return dt; + const performanceState = Object.assign(target, { + capturing: false, + runningAverageTasksPerSecond: 0, + averagesTaken: [], + runningAverageLeadTime: -1, + averagesTakenLeadTime: [], + leadTimeQueue: [], + performance: { + numberOfTasksRanOverall: 0, + cycles: { + fillPoolStarts: 0, + fillPoolCycles: 0, + fillPoolBail: 0, + fillPoolBailNoTasks: 0, + }, + claimAvailableTasksNoTasks: 0, + claimAvailableTasksNoAvailableWorkers: 0, + elasticsearchApiCalls: { + timeUntilFirstRun: [], + timeUntilFirstMarkAsRun: [], + firstMarkAsRunningTillRan: [], + timeFromMarkAsRunTillRun: [], + timeFromRunTillNextMarkAsRun: [], + claimAvailableTasks: [], + }, + activityDuration: [], + sleepDuration: [], + taskPollerActivityDurationPreScheduleComplete: [], + taskPoolAttemptToRun: [], + taskRunnerMarkTaskAsRunning: [], + + summarize: [] + }, + }); + + performanceState.performanceObserver = new PerformanceObserver((list, observer) => { + list.getEntries().forEach(entry => { + const { name, duration } = entry; + switch (name) { + // Elasticsearch Api Calls + case 'perfTask.firstRun': + performanceState.performance.elasticsearchApiCalls.timeUntilFirstRun.push(duration); + break; + case 'perfTask.firstMarkAsRunning': + performanceState.performance.elasticsearchApiCalls.timeUntilFirstMarkAsRun.push(duration); + break; + case 'perfTask.firstMarkAsRunningTillRan': + performanceState.performance.elasticsearchApiCalls.firstMarkAsRunningTillRan.push(duration); + break; + case 'perfTask.markUntilRun': + performanceState.performance.elasticsearchApiCalls.timeFromMarkAsRunTillRun.push(duration); + break; + case 'perfTask.runUntilNextMarkAsRunning': + performanceState.performance.elasticsearchApiCalls.timeFromRunTillNextMarkAsRun.push(duration); + break; + case 'claimAvailableTasks': + performanceState.performance.elasticsearchApiCalls.claimAvailableTasks.push(duration); + break; + case 'TaskPoller.sleepDuration': + performanceState.performance.sleepDuration.push(duration); + break; + case 'fillPool.activityDurationUntilNoTasks': + performanceState.performance.activityDuration.push(duration); + break; + case 'fillPool.activityDurationUntilExhaustedCapacity': + performanceState.performance.activityDuration.push(duration); + break; + case 'fillPool.bailExhaustedCapacity': + performanceState.performance.cycles.fillPoolBail++; + break; + case 'fillPool.bailNoTasks': + performanceState.performance.cycles.fillPoolBail++; + performanceState.performance.cycles.fillPoolBailNoTasks++; + break; + case 'fillPool.start': + performanceState.performance.cycles.fillPoolStarts++; + break; + case 'fillPool.cycle': + performanceState.performance.cycles.fillPoolCycles++; + break; + break; + case 'claimAvailableTasks.noTasks': + performanceState.performance.claimAvailableTasksNoTasks++; + break; + case 'claimAvailableTasks.noAvailableWorkers': + performanceState.performance.claimAvailableTasksNoAvailableWorkers++; + break; + case 'taskPool.attemptToRun': + performanceState.performance.taskPoolAttemptToRun.push(duration); + break; + case 'taskRunner.markTaskAsRunning': + performanceState.performance.taskRunnerMarkTaskAsRunning.push(duration); + break; + case 'perfTest.duration': + observer.disconnect(); + const { summarize } = performanceState.performance; + if(summarize && summarize.length) { + summarize.splice(0, summarize.length).forEach(([resolve, summarize]) => { + resolve(summarize(duration)); + }); + } + break; + default: + if (name.startsWith('perfTask_run_')) { + performanceState.performance.numberOfTasksRanOverall++; + } + } + }); + }); + performanceState.performanceObserver.observe({ entryTypes: ['measure', 'mark'] }); + + return performanceState; } diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js index b9fe788093d117..ca6d8707f5c58f 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js @@ -5,6 +5,7 @@ */ import Joi from 'joi'; +import { range, chunk } from 'lodash'; const scope = 'perf-testing'; export function initRoutes(server, performanceState) { @@ -18,32 +19,56 @@ export function initRoutes(server, performanceState) { payload: Joi.object({ tasksToSpawn: Joi.number().required(), durationInSeconds: Joi.number().required(), - trackExecutionTimeline: Joi.boolean().default(false).required(), + trackExecutionTimeline: Joi.boolean() + .default(false) + .required(), }), }, }, async handler(request) { - const { tasksToSpawn, durationInSeconds, trackExecutionTimeline } = request.payload; - const tasks = []; + performanceState.capture(); - for (let taskIndex = 0; taskIndex < tasksToSpawn; taskIndex++) { - tasks.push( - await taskManager.schedule( - { - taskType: 'performanceTestTask', - params: { taskIndex, trackExecutionTimeline }, - scope: [scope], - }, - { request } + const { tasksToSpawn, durationInSeconds, trackExecutionTimeline } = request.payload; + const startAt = millisecondsFromNow(5000).getTime(); + await chunk(range(tasksToSpawn), 200) + .map(chunkOfTasksToSpawn => () => + Promise.all( + chunkOfTasksToSpawn.map(taskIndex => + taskManager.schedule( + { + taskType: 'performanceTestTask', + params: { + startAt, + taskIndex, + trackExecutionTimeline, + runUntil: millisecondsFromNow(durationInSeconds * 1000).getTime(), + }, + scope: [scope], + }, + { request } + ) + ) ) - ); - } + ) + .reduce((chain, nextExecutor) => { + return chain.then(() => nextExecutor()); + }, Promise.resolve()); return new Promise(resolve => { setTimeout(() => { - resolve(performanceState); - }, durationInSeconds * 1000); + performanceState.endCapture().then(resolve); + }, durationInSeconds * 1000 + 10000 /* wait extra 10s to drain queue */); }); }, }); } + +function millisecondsFromNow(ms) { + if (!ms) { + return; + } + + const dt = new Date(); + dt.setTime(dt.getTime() + ms); + return dt; +} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json index 22ee21852aeee9..7d46d6b0f3cca6 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json @@ -7,6 +7,10 @@ }, "license": "Apache-2.0", "dependencies": { - "joi": "^13.5.2" + "lodash": "^4.17.15", + "uuid": "3.3.2", + "joi": "^13.5.2", + "stats-lite": "2.2.0", + "pretty-ms": "5.0.0" } } diff --git a/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts b/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts index e8dbf7c5092878..d5caff5f9d10bf 100644 --- a/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts +++ b/x-pack/test/plugin_api_perf/test_suites/task_manager/task_manager_perf_integration.ts @@ -10,23 +10,151 @@ export default function({ getService }: { getService: (service: string) => any } const log = getService('log'); const supertest = getService('supertest'); + const params = { tasksToSpawn: 100, trackExecutionTimeline: true, durationInSeconds: 60 }; describe('stressing task manager', () => { - it('should run 10 tasks every second for a minute', async () => { - const { runningAverageTasks, runningAverageLeadTime } = await supertest + it(`should run ${params.tasksToSpawn} tasks over ${params.durationInSeconds} seconds`, async () => { + const { + runningAverageTasksPerSecond, + runningAverageLeadTime, + // how often things happen in Task Manager + cycles: { fillPoolStarts, fillPoolCycles, fillPoolBail, fillPoolBailNoTasks }, + claimAvailableTasksNoTasks, + claimAvailableTasksNoAvailableWorkers, + numberOfTasksRanOverall, + // how long it takes to talk to Elasticsearch + elasticsearchApiCalls: { + timeUntilFirstMarkAsRun, + firstMarkAsRunningTillRan, + timeFromMarkAsRunTillRun, + timeFromRunTillNextMarkAsRun, + claimAvailableTasks, + }, + // durations in Task Manager + perfTestDuration, + taskPoolAttemptToRun, + taskRunnerMarkTaskAsRunning, + sleepDuration, + activityDuration, + } = await supertest .post('/api/perf_tasks') .set('kbn-xsrf', 'xxx') - .send({ tasksToSpawn: 20, trackExecutionTimeline: true, durationInSeconds: 60 }) + .send(params) .expect(200) .then((response: any) => response.body); - log.debug(`Stress Test Result:`); - log.debug(`Average number of tasks executed per second: ${runningAverageTasks}`); - log.debug( - `Average time it took from the moment a task's scheduled time was reached, until Task Manager picked it up: ${runningAverageLeadTime}` + log.info(cyan(`Stress Test Result:`)); + log.info( + `Average number of tasks executed per second: ${bright(runningAverageTasksPerSecond)}` ); + log.info( + `Average time between a task's "runAt" scheduled time and the time it actually ran: ${bright( + runningAverageLeadTime + )}` + ); + + if (params.trackExecutionTimeline) { + log.info( + `Overall number of tasks ran in ${bright(params.durationInSeconds)} seconds: ${bright( + numberOfTasksRanOverall + )}` + ); + log.info(`Average time between stages:`); + log.info( + `Schedule ---[${descMetric( + timeUntilFirstMarkAsRun + )}]--> first markAsRunning ---[${descMetric(firstMarkAsRunningTillRan)}]--> first run` + ); + log.info( + `markAsRunning ---[${descMetric(timeFromMarkAsRunTillRun)}]--> run ---[${descMetric( + timeFromRunTillNextMarkAsRun + )}]---> next markAsRunning` + ); + log.info(`Duration of Perf Test: ${bright(perfTestDuration)}`); + log.info(`Activity within Task Poller: ${bright(activityDuration)}`); + log.info(`Inactivity due to Sleep: ${bright(sleepDuration)}`); + log.info(`Polling Cycles: ${colorizeCycles(fillPoolStarts, fillPoolCycles, fillPoolBail)}`); + if (fillPoolBail > 0) { + log.info(` ⮑ Bailed due to:`); + if (fillPoolBailNoTasks > 0) { + log.info(` ⮑ No Tasks To Process:`); + if (claimAvailableTasksNoTasks > 0) { + log.info(` ⮑ ${claimAvailableTasksNoTasks} Times, due to No Tasks Claimed`); + } + if (claimAvailableTasksNoAvailableWorkers > 0) { + log.info( + ` ⮑ ${claimAvailableTasksNoAvailableWorkers} Times, due to having No Available Worker Capacity` + ); + } + } + if (fillPoolBail - fillPoolBailNoTasks > 0) { + log.info( + ` ⮑ Exhausted Available Workers due to on going Task runs ${fillPoolBail - + fillPoolBailNoTasks}` + ); + } + } + log.info( + `average duration taken to Claim Available Tasks: ${descMetric(claimAvailableTasks)}` + ); + log.info( + `average duration taken to Mark claimed Tasks as Running in Task Pool: ${descMetric( + taskPoolAttemptToRun + )}` + ); + log.info( + `average duration taken to Mark individual Tasks as Running in Task Runner: ${descMetric( + taskRunnerMarkTaskAsRunning + )}` + ); + } - expect(runningAverageTasks).to.be.greaterThan(0); + expect(runningAverageTasksPerSecond).to.be.greaterThan(0); expect(runningAverageLeadTime).to.be.greaterThan(0); }); }); } + +function descMetric(metric: { mean: number; range: { min: number; max: number } }): string { + return `${colorize(metric.mean)}ms ${dim(`(`)}${colorize(metric.range.min)}${dim( + `ms - ` + )}${colorize(metric.range.max)}${dim(`ms)`)}`; +} + +function colorize(avg: number) { + if (!avg) { + return red('?'); + } + return avg < 500 ? green(`${avg}`) : avg < 1000 ? cyan(`${avg}`) : red(`${avg}`); +} + +function colorizeCycles(fillPoolStarts: number, fillPoolCycles: number, fillPoolBail: number) { + const perc = (fillPoolCycles * 100) / fillPoolStarts; + const colorFunc = perc >= 100 ? green : perc >= 50 ? cyan : red; + return ( + `ran ` + + bright(`${fillPoolStarts}`) + + ` cycles, of which ` + + colorFunc(`${fillPoolCycles}`) + + ` were reran before bailing` + ); +} + +function cyan(str: string) { + return `\x1b[36m${str}\x1b[0m`; +} + +function red(str: string) { + return `\x1b[31m${str}\x1b[0m`; +} + +function green(str: string) { + return `\x1b[32m${str}\x1b[0m`; +} + +function dim(str: string) { + return `\x1b[2m${str}\x1b[0m`; +} + +function bright(str: string | number) { + return `\x1b[1m${str}\x1b[0m`; +} diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 925d28b62f9bb8..276b42423860cb 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -14,7 +14,7 @@ import { getLogoutRequest, getSAMLRequestId, getSAMLResponse } from '../../fixtu import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService }: FtrProviderContext) { - const chance = getService('chance'); + const randomness = getService('randomness'); const supertest = getService('supertestWithoutAuth'); const config = getService('config'); @@ -23,7 +23,7 @@ export default function({ getService }: FtrProviderContext) { function createSAMLResponse(options = {}) { return getSAMLResponse({ destination: `http://localhost:${kibanaServerConfig.port}/api/security/saml/callback`, - sessionIndex: chance.natural(), + sessionIndex: String(randomness.naturalNumber()), ...options, }); } @@ -365,7 +365,7 @@ export default function({ getService }: FtrProviderContext) { const handshakeCookie = request.cookie(handshakeResponse.headers['set-cookie'][0])!; const samlRequestId = await getSAMLRequestId(handshakeResponse.headers.location); - idpSessionIndex = chance.natural(); + idpSessionIndex = String(randomness.naturalNumber()); const samlAuthenticationResponse = await supertest .post('/api/security/saml/callback') .set('kbn-xsrf', 'xxx') diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts index 8fb807016afeb6..9d3029db013d3c 100644 --- a/x-pack/test/saml_api_integration/config.ts +++ b/x-pack/test/saml_api_integration/config.ts @@ -20,7 +20,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { testFiles: [require.resolve('./apis')], servers: xPackAPITestsConfig.get('servers'), services: { - chance: kibanaAPITestsConfig.get('services.chance'), + randomness: kibanaAPITestsConfig.get('services.randomness'), es: kibanaAPITestsConfig.get('services.es'), supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'), }, diff --git a/x-pack/test/saml_api_integration/services.ts b/x-pack/test/saml_api_integration/services.ts index 3cbe8fb37356af..307108fa097f40 100644 --- a/x-pack/test/saml_api_integration/services.ts +++ b/x-pack/test/saml_api_integration/services.ts @@ -7,7 +7,7 @@ import { services as apiIntegrationServices } from '../api_integration/services'; export const services = { - chance: apiIntegrationServices.chance, + randomness: apiIntegrationServices.randomness, es: apiIntegrationServices.es, supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, }; diff --git a/x-pack/test/typings/hapi.d.ts b/x-pack/test/typings/hapi.d.ts index f12e3c0c1c7639..339819798b480e 100644 --- a/x-pack/test/typings/hapi.d.ts +++ b/x-pack/test/typings/hapi.d.ts @@ -7,7 +7,6 @@ import 'hapi'; import { CloudPlugin } from '../../legacy/plugins/cloud'; -import { EncryptedSavedObjectsPlugin } from '../../legacy/plugins/encrypted_saved_objects'; import { XPackMainPlugin } from '../../legacy/plugins/xpack_main/xpack_main'; import { SecurityPlugin } from '../../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../../legacy/plugins/actions'; @@ -23,7 +22,6 @@ declare module 'hapi' { cloud?: CloudPlugin; xpack_main: XPackMainPlugin; security?: SecurityPlugin; - encrypted_saved_objects?: EncryptedSavedObjectsPlugin; actions?: ActionsPlugin; alerting?: AlertingPlugin; task_manager?: TaskManager; diff --git a/x-pack/test/visual_regression/tests/infra/index.js b/x-pack/test/visual_regression/tests/infra/index.js index 9efd45e83a5504..f3063746b37b55 100644 --- a/x-pack/test/visual_regression/tests/infra/index.js +++ b/x-pack/test/visual_regression/tests/infra/index.js @@ -7,7 +7,7 @@ export default function ({ loadTestFile, getService }) { const browser = getService('browser'); - describe('InfraUI Visual Regression', function () { + describe.skip('InfraUI Visual Regression', function () { before(async () => { await browser.setWindowSize(1600, 1000); }); diff --git a/x-pack/test/visual_regression/tests/login_page.js b/x-pack/test/visual_regression/tests/login_page.js index cfc53735dffcc9..003a23086c7b6c 100644 --- a/x-pack/test/visual_regression/tests/login_page.js +++ b/x-pack/test/visual_regression/tests/login_page.js @@ -11,8 +11,8 @@ export default function ({ getService, getPageObjects }) { const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'security']); - describe('Security', () => { - describe.skip('Login Page', () => { + describe.skip('Security', () => { + describe('Login Page', () => { before(async () => { await esArchiver.load('empty_kibana'); await PageObjects.security.logout(); diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts index c5c02ef3765a72..1c6ad3a55cb2c1 100644 --- a/x-pack/typings/hapi.d.ts +++ b/x-pack/typings/hapi.d.ts @@ -7,7 +7,6 @@ import 'hapi'; import { CloudPlugin } from '../legacy/plugins/cloud'; -import { EncryptedSavedObjectsPlugin } from '../legacy/plugins/encrypted_saved_objects'; import { XPackMainPlugin } from '../legacy/plugins/xpack_main/xpack_main'; import { SecurityPlugin } from '../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../legacy/plugins/actions'; @@ -23,7 +22,6 @@ declare module 'hapi' { cloud?: CloudPlugin; xpack_main: XPackMainPlugin; security?: SecurityPlugin; - encrypted_saved_objects?: EncryptedSavedObjectsPlugin; actions?: ActionsPlugin; alerting?: AlertingPlugin; task_manager?: TaskManager; diff --git a/yarn.lock b/yarn.lock index 6cbd920ab3970c..6526c90663b753 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1064,10 +1064,10 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@elastic/charts@^13.5.9": - version "13.5.9" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-13.5.9.tgz#8e27ec7de934e20a9b853921cd453a372be99ef4" - integrity sha512-H5xsW/tEpjZhm0FpZThMyjuVBWlcXF2ImpfTWYv13p8GKmorCyQWbePau9Ya8N3lMmkHUMH2e95ifd3K3g9RgA== +"@elastic/charts@^13.5.12": + version "13.5.12" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-13.5.12.tgz#95bd92389ec5fb411debfa5979091b6da2e4b123" + integrity sha512-MMNuebZ5jmzXkUJZr/mSvmtWNIR0gWGBtbqpZBfq3T9WRQPvnEHeE/N1WmXw2BSvwN86fy1i0gr52izh/nfzjQ== dependencies: "@types/d3-shape" "^1.3.1" classnames "^2.2.6" @@ -5785,6 +5785,11 @@ aws4@^1.6.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4= +axe-core@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.3.2.tgz#7baf3c55a5cf1621534a2c38735f5a1bf2f7e1a8" + integrity sha512-lRdxsRt7yNhqpcXQk1ao1BL73OZDzmFCWOG0mC4tGR/r14ohH2payjHwCMQjHGbBKm924eDlmG7utAGHiX/A6g== + axios@^0.18.0, axios@^0.18.1: version "0.18.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" @@ -5982,6 +5987,14 @@ babel-plugin-emotion@^9.2.11: source-map "^0.5.7" touch "^2.0.1" +babel-plugin-filter-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-3.0.0.tgz#a849683837ad29960da17492fb32789ab6b09a11" + integrity sha512-p/chjzVTgCxUqyLM0q/pfWVZS7IJTwGQMwNg0LOvuQpKiTftQgZDtkGB8XvETnUw19rRcL7bJCTopSwibTN2tA== + dependencies: + "@babel/types" "^7.4.0" + lodash "^4.17.11" + babel-plugin-istanbul@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz#7981590f1956d75d67630ba46f0c22493588c893" @@ -7710,10 +7723,10 @@ chrome-trace-event@^1.0.0, chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^77.0.0: - version "77.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-77.0.0.tgz#bd916cc87a0ccb7a6e4fb4b43cb2368bc54db6a0" - integrity sha512-mZa1IVx4HD8rDaItWbnS470mmypgiWsDiu98r0NkiT4uLm3qrANl4vOU6no6vtWtLQiW5kt1POcIbjeNpsLbXA== +chromedriver@^78.0.1: + version "78.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-78.0.1.tgz#2db3425a2cba6fcaf1a41d9538b16c3d06fa74a8" + integrity sha512-eOsyFk4xb9EECs1VMrDbxO713qN+Bu1XUE8K9AuePc3839TPdAegg72kpXSzkeNqRNZiHbnJUItIVCLFkDqceA== dependencies: del "^4.1.1" extract-zip "^1.6.7" @@ -10519,15 +10532,15 @@ ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== -elasticsearch-browser@^16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/elasticsearch-browser/-/elasticsearch-browser-16.4.0.tgz#0e0b9b781f9f5207498085ce1d47d916ff46fb82" - integrity sha512-VILNtZQytYnT3mYit3SeN+SgjqOw4piZEhUrHjl7bNY1OGU7nwODtVj/rL7bc0pTmVAnDTtq0CBKE+ncjd+3hQ== +elasticsearch-browser@^16.5.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/elasticsearch-browser/-/elasticsearch-browser-16.5.0.tgz#d2efbbf8751bb563e91b74117a14b9211df5cfe9" + integrity sha512-F7npcrmMi3OgQ4fL+7sYLZp5Y9MS6WobVdCA18d9/Eef06x3+UzVE+pRaM71y4/i0N5rt/QrvmijHt25wz5SLw== -elasticsearch@^16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.4.0.tgz#534d6d83a9e0cc3080bec92b89dac0eb1e4d4c73" - integrity sha512-uJN1hNNB8fBkaDqhC1SW8NbEC6Ge63fUHj0vJ9BZdHBlIhbsUq68Y5DUv6TRoE6IC8ezCDFqhRs7m7ar19+iiQ== +elasticsearch@^16.4.0, elasticsearch@^16.5.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.5.0.tgz#619a48040be25d345fdddf09fa6042a88c3974d6" + integrity sha512-9YbmU2AtM/kQdmp96EI5nu2bjxowdarV6IsKmcS+jQowJ3mhG98J1DCVOtEKuFvsnNaLyKD3aPbCAmb72+WX3w== dependencies: agentkeepalive "^3.4.1" chalk "^1.0.0" @@ -24592,41 +24605,13 @@ rxjs@^5.0.0-beta.11, rxjs@^5.5.0, rxjs@^5.5.2: dependencies: symbol-observable "1.0.1" -rxjs@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.1.0.tgz#833447de4e4f6427b9cec3e5eb9f56415cd28315" - integrity sha512-lMZdl6xbHJCSb5lmnb6nOhsoBVCyoDC5LDJQK9WWyq+tsI7KnlDIZ0r0AZAlBpRPLbwQA9kzSBAZwNIZEZ+hcw== - dependencies: - tslib "^1.9.0" - -rxjs@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1" - integrity sha512-OwMxHxmnmHTUpgO+V7dZChf3Tixf4ih95cmXjzzadULziVl/FKhHScGLj4goEw9weePVOH2Q0+GcCBUhKCZc/g== - dependencies: - tslib "^1.9.0" - -rxjs@^6.3.3: +rxjs@^6.1.0, rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== dependencies: tslib "^1.9.0" -rxjs@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504" - integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== - dependencies: - tslib "^1.9.0" - -rxjs@^6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" - integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== - dependencies: - tslib "^1.9.0" - safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"